aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--integration-tests/gradle/projects/it-multimodule-1/second/build.gradle2
-rw-r--r--integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt1
-rw-r--r--integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt2
-rw-r--r--integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt2
-rw-r--r--plugins/all-modules-page/build.gradle.kts9
-rw-r--r--plugins/all-modules-page/src/main/kotlin/AllModulesPageGeneration.kt12
-rw-r--r--plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt42
-rw-r--r--plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt (renamed from plugins/all-modules-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt)20
-rw-r--r--plugins/all-modules-page/src/main/kotlin/MultimoduleLocationProvider.kt1
-rw-r--r--plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt10
-rw-r--r--plugins/all-modules-page/src/main/kotlin/ResolveLinkCommandHandler.kt45
-rw-r--r--plugins/all-modules-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt141
-rw-r--r--plugins/all-modules-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt18
-rw-r--r--plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkCommandResolutionTest.kt)5
-rw-r--r--plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkGfmCommandResolutionTest.kt)8
-rw-r--r--plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/mockedPackageListFactory.kt)0
-rw-r--r--plugins/base/base-test-utils/build.gradle.kts1
-rw-r--r--plugins/base/base-test-utils/src/main/kotlin/utils/assertHtmlEqualsIgnoringWhitespace.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/assertHtmlEqualsIgnoringWhitespace.kt)2
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt8
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt2
-rw-r--r--plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt12
-rw-r--r--plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt2
-rw-r--r--plugins/gfm/gfm-template-processing/build.gradle.kts1
-rw-r--r--plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingPlugin.kt6
-rw-r--r--plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt40
-rw-r--r--plugins/templating/build.gradle.kts18
-rw-r--r--plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt56
-rw-r--r--plugins/templating/src/main/kotlin/templates/CommandHandler.kt11
-rw-r--r--plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt41
-rw-r--r--plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt13
-rw-r--r--plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt (renamed from plugins/all-modules-page/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt)46
-rw-r--r--plugins/templating/src/main/kotlin/templates/PathToRootSubstitutor.kt (renamed from plugins/all-modules-page/src/main/kotlin/templates/PathToRootSubstitutor.kt)4
-rw-r--r--plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt60
-rw-r--r--plugins/templating/src/main/kotlin/templates/Substitutor.kt (renamed from plugins/all-modules-page/src/main/kotlin/templates/Substitutor.kt)2
-rw-r--r--plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt (renamed from plugins/all-modules-page/src/main/kotlin/templates/TemplateProcessor.kt)14
-rw-r--r--plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt50
-rw-r--r--plugins/templating/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
-rw-r--r--plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToNavigationCommandResolutionTest.kt)6
-rw-r--r--plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToSearchCommandResolutionTest.kt)5
-rw-r--r--plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt (renamed from plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/SubstitutionCommandResolutionTest.kt)6
-rw-r--r--plugins/templating/src/test/kotlin/templates/TemplatingDokkaTestGenerator.kt63
-rw-r--r--plugins/templating/src/test/kotlin/templates/TestTemplatingGeneration.kt22
-rw-r--r--plugins/templating/src/test/kotlin/templates/TestTemplatingPlugin.kt16
-rw-r--r--plugins/versioning/build.gradle.kts19
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/ReplaceVersionCommandConsumer.kt45
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/ReplaceVersionsCommand.kt26
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/VersioningConfiguration.kt20
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt101
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/VersioningPlugin.kt39
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/VersionsNavigationCreator.kt53
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/VersionsOrdering.kt23
-rw-r--r--plugins/versioning/src/main/kotlin/versioning/htmlPreprocessors.kt24
-rw-r--r--plugins/versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
-rw-r--r--plugins/versioning/src/main/resources/dokka/styles/multimodule.css37
-rw-r--r--runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAutoConfigurationTest.kt4
-rw-r--r--settings.gradle.kts2
56 files changed, 923 insertions, 297 deletions
diff --git a/integration-tests/gradle/projects/it-multimodule-1/second/build.gradle b/integration-tests/gradle/projects/it-multimodule-1/second/build.gradle
index 188917bc..2b62f963 100644
--- a/integration-tests/gradle/projects/it-multimodule-1/second/build.gradle
+++ b/integration-tests/gradle/projects/it-multimodule-1/second/build.gradle
@@ -6,7 +6,7 @@ dokkaHtml {
dokkaSourceSets {
"main" {
externalDocumentationLink {
- url.set(new URL("file://" + rootProject.rootDir.toPath().toAbsolutePath().resolve("first/build/dokka/html/first/")))
+ url.set(new URL("file://" + rootProject.rootDir.toPath().toAbsolutePath().resolve("first/build/dokka/html/")))
packageListUrl.set(new URL("file://" + rootProject.rootDir.toPath().toAbsolutePath().resolve("first/build/dokka/html/first/package-list")))
}
}
diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt
index bfeed55e..216ff395 100644
--- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt
+++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt
@@ -26,7 +26,6 @@ class Android0GradleIntegrationTest(override val versions: BuildVersions) : Abst
)
}
-
@BeforeTest
fun prepareProjectFiles() {
assumeAndroidSdkInstalled()
diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt
index 33b4885e..d51f27e6 100644
--- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt
+++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt
@@ -67,7 +67,7 @@ class BasicGradleIntegrationTest(override val versions: BuildVersions) : Abstrac
val moduleOutputDir = File(this, "-basic -project")
assertTrue(moduleOutputDir.isDirectory, "Missing module directory")
- val moduleIndexHtml = File(moduleOutputDir, "index.html")
+ val moduleIndexHtml = File(this, "index.html")
assertTrue(moduleIndexHtml.isFile, "Missing module index.html")
val modulePackageDir = File(moduleOutputDir, "it.basic")
diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt
index 0e125258..9746c8b8 100644
--- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt
+++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt
@@ -65,7 +65,7 @@ class BasicGroovyIntegrationTest(override val versions: BuildVersions) : Abstrac
val moduleOutputDir = File(this, "it-basic-groovy")
assertTrue(moduleOutputDir.isDirectory, "Missing module directory")
- val moduleIndexHtml = File(moduleOutputDir, "index.html")
+ val moduleIndexHtml = File(this, "index.html")
assertTrue(moduleIndexHtml.isFile, "Missing module index.html")
val modulePackageDir = File(moduleOutputDir, "it.basic")
diff --git a/plugins/all-modules-page/build.gradle.kts b/plugins/all-modules-page/build.gradle.kts
index c6e88574..9c5fe1c3 100644
--- a/plugins/all-modules-page/build.gradle.kts
+++ b/plugins/all-modules-page/build.gradle.kts
@@ -6,6 +6,8 @@ registerDokkaArtifactPublication("dokkaAllModulesPage") {
dependencies {
implementation(project(":plugins:base"))
+ implementation(project(":plugins:templating"))
+ implementation(project(":plugins:versioning"))
testImplementation(project(":plugins:base"))
testImplementation(project(":plugins:base:base-test-utils"))
testImplementation(project(":plugins:gfm"))
@@ -13,10 +15,9 @@ dependencies {
val coroutines_version: String by project
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
-
- implementation("org.jsoup:jsoup:1.12.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1")
-
val kotlinx_html_version: String by project
- testImplementation("org.jetbrains.kotlinx:kotlinx-html-jvm:$kotlinx_html_version")
+ implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:$kotlinx_html_version")
+
+ implementation("org.jsoup:jsoup:1.12.1")
} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/main/kotlin/AllModulesPageGeneration.kt b/plugins/all-modules-page/src/main/kotlin/AllModulesPageGeneration.kt
index 5ac854b4..0013feed 100644
--- a/plugins/all-modules-page/src/main/kotlin/AllModulesPageGeneration.kt
+++ b/plugins/all-modules-page/src/main/kotlin/AllModulesPageGeneration.kt
@@ -8,15 +8,23 @@ import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.query
import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.templates.TemplatingPlugin
+import org.jetbrains.dokka.versioning.VersioningPlugin
class AllModulesPageGeneration(private val context: DokkaContext) : Generation {
private val allModulesPagePlugin by lazy { context.plugin<AllModulesPagePlugin>() }
+ private val templatingPlugin by lazy { context.plugin<TemplatingPlugin>() }
+ private val versioningPlugin by lazy { context.plugin<VersioningPlugin>() }
override fun Timer.generate() {
+
report("Creating all modules page")
val pages = createAllModulesPage()
+ report("Copy previous documentation")
+ handlePreviousDocs()
+
report("Transforming pages")
val transformedPages = transformAllModulesPage(pages)
@@ -29,6 +37,8 @@ class AllModulesPageGeneration(private val context: DokkaContext) : Generation {
override val generationName = "index page for project"
+ fun handlePreviousDocs() = versioningPlugin.querySingle { versioningHandler }.invoke()
+
fun createAllModulesPage() = allModulesPagePlugin.querySingle { allModulesPageCreator }.invoke()
fun transformAllModulesPage(pages: RootPageNode) =
@@ -39,5 +49,5 @@ class AllModulesPageGeneration(private val context: DokkaContext) : Generation {
}
fun processSubmodules() =
- allModulesPagePlugin.querySingle { templateProcessor }.process()
+ templatingPlugin.querySingle { templateProcessor }.process()
} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt
index c99293ef..9f4b62ee 100644
--- a/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt
+++ b/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt
@@ -1,31 +1,29 @@
package org.jetbrains.dokka.allModulesPage
import org.jetbrains.dokka.CoreExtensions
-import org.jetbrains.dokka.allModulesPage.templates.*
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProviderFactory
import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.templates.TemplatingPlugin
import org.jetbrains.dokka.transformers.pages.PageCreator
import org.jetbrains.dokka.transformers.pages.PageTransformer
class AllModulesPagePlugin : DokkaPlugin() {
- val templateProcessor by extensionPoint<TemplateProcessor>()
- val templateProcessingStrategy by extensionPoint<TemplateProcessingStrategy>()
val partialLocationProviderFactory by extensionPoint<LocationProviderFactory>()
val allModulesPageCreator by extensionPoint<PageCreator>()
val allModulesPageTransformer by extensionPoint<PageTransformer>()
val externalModuleLinkResolver by extensionPoint<ExternalModuleLinkResolver>()
- val substitutor by extensionPoint<Substitutor>()
-
val allModulesPageCreators by extending {
allModulesPageCreator providing ::MultimodulePageCreator
}
+ val dokkaBase by lazy { plugin<DokkaBase>() }
+
val multimoduleLocationProvider by extending {
- (plugin<DokkaBase>().locationProviderFactory
+ (dokkaBase.locationProviderFactory
providing MultimoduleLocationProvider::Factory
override plugin<DokkaBase>().locationProvider)
}
@@ -37,37 +35,11 @@ class AllModulesPagePlugin : DokkaPlugin() {
val allModulesPageGeneration by extending {
(CoreExtensions.generation
providing ::AllModulesPageGeneration
- override plugin<DokkaBase>().singleGeneration)
- }
-
- val defaultTemplateProcessor by extending {
- templateProcessor providing ::DefaultTemplateProcessor
- }
-
- val directiveBasedHtmlTemplateProcessingStrategy by extending {
- templateProcessingStrategy providing ::DirectiveBasedHtmlTemplateProcessingStrategy order {
- before(fallbackProcessingStrategy)
- }
- }
-
- val fallbackProcessingStrategy by extending {
- templateProcessingStrategy providing ::FallbackTemplateProcessingStrategy
- }
-
- val navigationSearchTemplateStrategy by extending {
- templateProcessingStrategy providing ::NavigationSearchTemplateStrategy order {
- before(fallbackProcessingStrategy)
- }
- }
-
- val pagesSearchTemplateStrategy by extending {
- templateProcessingStrategy providing ::PagesSearchTemplateStrategy order {
- before(fallbackProcessingStrategy)
- }
+ override dokkaBase.singleGeneration)
}
- val pathToRootSubstitutor by extending {
- substitutor providing ::PathToRootSubstitutor
+ val resolveLinkCommandHandler by extending {
+ plugin<TemplatingPlugin>().directiveBasedCommandHandlers providing ::ResolveLinkCommandHandler
}
val multiModuleLinkResolver by extending {
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt b/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt
index d0e787b6..74513957 100644
--- a/plugins/all-modules-page/src/main/kotlin/templates/ExternalModuleLinkResolver.kt
+++ b/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.allModulesPage
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.DokkaBase
@@ -30,14 +30,22 @@ class DefaultExternalModuleLinkResolver(val context: DokkaContext) : ExternalMod
context.configuration.modules.map(::loadPackageListForModule).toMap()
return packageLists.mapNotNull { (module, packageList) ->
packageList?.let {
- ExternalDocumentation(
- URL("file:/${module.name}/${module.name}"),
- packageList
- )
+ context.configuration.modules.find { it.name == module.name }?.let { m ->
+ ExternalDocumentation(
+ URL("file:/${m.relativePathToOutputDirectory.toRelativeOutputDir()}"),
+ packageList
+ )
+ }
}
}
}
+ private fun File.toRelativeOutputDir(): File = if(isAbsolute) {
+ relativeToOrSelf(context.configuration.outputDir)
+ } else {
+ this
+ }
+
private fun loadPackageListForModule(module: DokkaConfiguration.DokkaModuleDescription) =
module.sourceOutputDirectory.resolve(File(identifierToFilename(module.name))).let {
it to PackageList.load(
@@ -71,7 +79,7 @@ class DefaultExternalModuleLinkResolver(val context: DokkaContext) : ExternalMod
RecognizedLinkFormat.DokkaJekyll -> ".md"
else -> ""
}
- "${module.relativePathToOutputDirectory}/${identifierToFilename(moduleName)}/index$extension"
+ "${module.relativePathToOutputDirectory}/index$extension"
}
}
diff --git a/plugins/all-modules-page/src/main/kotlin/MultimoduleLocationProvider.kt b/plugins/all-modules-page/src/main/kotlin/MultimoduleLocationProvider.kt
index 1dbbd386..c7c32d55 100644
--- a/plugins/all-modules-page/src/main/kotlin/MultimoduleLocationProvider.kt
+++ b/plugins/all-modules-page/src/main/kotlin/MultimoduleLocationProvider.kt
@@ -1,7 +1,6 @@
package org.jetbrains.dokka.allModulesPage
import org.jetbrains.dokka.allModulesPage.MultimodulePageCreator.Companion.MULTIMODULE_PACKAGE_PLACEHOLDER
-import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider
import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
import org.jetbrains.dokka.links.DRI
diff --git a/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt b/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt
index a333d7c4..458cf4c1 100644
--- a/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt
+++ b/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumen
import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentation
import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentationFragments
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
+import org.jetbrains.dokka.base.templating.InsertTemplateExtra
import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter
import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
import org.jetbrains.dokka.links.DRI
@@ -17,10 +18,14 @@ import org.jetbrains.dokka.model.doc.P
import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.configuration
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.transformers.pages.PageCreator
import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.dokka.versioning.ReplaceVersionsCommand
+import org.jetbrains.dokka.versioning.VersioningConfiguration
+import org.jetbrains.dokka.versioning.VersioningPlugin
class MultimodulePageCreator(
private val context: DokkaContext,
@@ -39,6 +44,11 @@ class MultimodulePageCreator(
kind = ContentKind.Cover,
sourceSets = sourceSetData
) {
+ /* The line below checks if there is a provided configuration for versioning.
+ If not, we are skipping the template for inserting versions navigation */
+ configuration<VersioningPlugin, VersioningConfiguration>(context)?.let {
+ group(extra = PropertyContainer.withAll(InsertTemplateExtra(ReplaceVersionsCommand))) { }
+ }
header(2, "All modules:")
table(styles = setOf(MultimoduleTable)) {
modules.forEach { module ->
diff --git a/plugins/all-modules-page/src/main/kotlin/ResolveLinkCommandHandler.kt b/plugins/all-modules-page/src/main/kotlin/ResolveLinkCommandHandler.kt
new file mode 100644
index 00000000..e881a5ab
--- /dev/null
+++ b/plugins/all-modules-page/src/main/kotlin/ResolveLinkCommandHandler.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.allModulesPage
+
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.base.templating.ResolveLinkCommand
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.templates.CommandHandler
+import org.jsoup.nodes.Attributes
+import org.jsoup.nodes.Element
+import org.jsoup.parser.Tag
+import java.io.File
+
+class ResolveLinkCommandHandler(context: DokkaContext) : CommandHandler {
+
+ private val externalModuleLinkResolver =
+ context.plugin<AllModulesPagePlugin>().querySingle { externalModuleLinkResolver }
+
+ override fun handleCommand(element: Element, command: Command, input: File, output: File) {
+ command as ResolveLinkCommand
+ val link = externalModuleLinkResolver.resolve(command.dri, output)
+ if (link == null) {
+ val children = element.childNodes().toList()
+ val attributes = Attributes().apply {
+ put("data-unresolved-link", command.dri.toString())
+ }
+ val el = Element(Tag.valueOf("span"), "", attributes).apply {
+ children.forEach { ch -> appendChild(ch) }
+ }
+ element.replaceWith(el)
+ return
+ }
+
+ val attributes = Attributes().apply {
+ put("href", link)
+ }
+ val children = element.childNodes().toList()
+ val el = Element(Tag.valueOf("a"), "", attributes).apply {
+ children.forEach { ch -> appendChild(ch) }
+ }
+ element.replaceWith(el)
+ }
+
+ override fun canHandle(command: Command): Boolean = command is ResolveLinkCommand
+} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt b/plugins/all-modules-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
deleted file mode 100644
index 2b065731..00000000
--- a/plugins/all-modules-page/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.jetbrains.dokka.allModulesPage.templates
-
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin
-import org.jetbrains.dokka.base.templating.*
-import org.jetbrains.dokka.plugability.DokkaContext
-import org.jetbrains.dokka.plugability.plugin
-import org.jetbrains.dokka.plugability.query
-import org.jetbrains.dokka.plugability.querySingle
-import org.jsoup.Jsoup
-import org.jsoup.nodes.*
-import org.jsoup.parser.Tag
-import java.io.File
-import java.nio.file.Files
-import java.util.concurrent.ConcurrentHashMap
-
-class DirectiveBasedHtmlTemplateProcessingStrategy(private val context: DokkaContext) : TemplateProcessingStrategy {
- private val navigationFragments = ConcurrentHashMap<String, Element>()
-
- private val substitutors = context.plugin<AllModulesPagePlugin>().query { substitutor }
- private val externalModuleLinkResolver = context.plugin<AllModulesPagePlugin>().querySingle { externalModuleLinkResolver }
-
- override suspend fun process(input: File, output: File): Boolean = coroutineScope {
- if (input.extension == "html") {
- launch {
- val document = withContext(IO) { Jsoup.parse(input, "UTF-8") }
- document.outputSettings().indentAmount(0).prettyPrint(false)
- document.select("dokka-template-command").forEach {
- when (val command = parseJson<Command>(it.attr("data"))) {
- is ResolveLinkCommand -> resolveLink(it, command, output)
- is AddToNavigationCommand -> navigationFragments[command.moduleName] = it
- is SubstitutionCommand -> substitute(it, TemplatingContext(input, output, it, command))
- else -> context.logger.warn("Unknown templating command $command")
- }
- }
- withContext(IO) { Files.write(output.toPath(), listOf(document.outerHtml())) }
- }
- true
- } else false
- }
-
- private fun substitute(element: Element, commandContext: TemplatingContext<SubstitutionCommand>) {
- val regex = commandContext.command.pattern.toRegex()
- element.children().forEach { it.traverseToSubstitute(regex, commandContext) }
-
- val childrenCopy = element.children().toList()
- val position = element.elementSiblingIndex()
- val parent = element.parent()
- element.remove()
-
- parent.insertChildren(position, childrenCopy)
- }
-
- private fun Node.traverseToSubstitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) {
- when (this) {
- is TextNode -> replaceWith(TextNode(wholeText.substitute(regex, commandContext)))
- is DataNode -> replaceWith(DataNode(wholeData.substitute(regex, commandContext)))
- is Element -> {
- attributes().forEach { attr(it.key, it.value.substitute(regex, commandContext)) }
- childNodes().forEach { it.traverseToSubstitute(regex, commandContext) }
- }
- }
- }
-
- private fun String.substitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) = buildString {
- var lastOffset = 0
- regex.findAll(this@substitute).forEach { match ->
- append(this@substitute, lastOffset, match.range.first)
- append(findSubstitution(commandContext, match))
- lastOffset = match.range.last + 1
- }
- append(this@substitute, lastOffset, this@substitute.length)
- }
-
- private fun findSubstitution(commandContext: TemplatingContext<SubstitutionCommand>, match: MatchResult): String =
- substitutors.asSequence().mapNotNull { it.trySubstitute(commandContext, match) }.firstOrNull() ?: match.value
-
- override suspend fun finish(output: File) {
- if (navigationFragments.isNotEmpty()) {
- val attributes = Attributes().apply {
- put("class", "sideMenu")
- }
- val node = Element(Tag.valueOf("div"), "", attributes)
- navigationFragments.entries.sortedBy { it.key }.forEach { (moduleName, command) ->
- command.select("a").forEach { a ->
- a.attr("href")?.also { a.attr("href", "${moduleName}/${it}") }
- }
- command.childNodes().toList().forEachIndexed { index, child ->
- if (index == 0) {
- child.attr("id", "$moduleName-nav-submenu")
- }
- node.appendChild(child)
- }
- }
-
- withContext(IO) {
- Files.write(output.resolve("navigation.html").toPath(), listOf(node.outerHtml()))
- }
-
- node.select("a").forEach { a ->
- a.attr("href")?.also { a.attr("href", "../${it}") }
- }
- navigationFragments.keys.forEach {
- withContext(IO) {
- Files.write(
- output.resolve(it).resolve("navigation.html").toPath(),
- listOf(node.outerHtml())
- )
- }
- }
- }
- }
-
- private fun resolveLink(it: Element, command: ResolveLinkCommand, fileContext: File) {
-
- val link = externalModuleLinkResolver.resolve(command.dri, fileContext)
- if (link == null) {
- val children = it.childNodes().toList()
- val attributes = Attributes().apply {
- put("data-unresolved-link", command.dri.toString())
- }
- val element = Element(Tag.valueOf("span"), "", attributes).apply {
- children.forEach { ch -> appendChild(ch) }
- }
- it.replaceWith(element)
- return
- }
-
- val attributes = Attributes().apply {
- put("href", link)
- }
- val children = it.childNodes().toList()
- val element = Element(Tag.valueOf("a"), "", attributes).apply {
- children.forEach { ch -> appendChild(ch) }
- }
- it.replaceWith(element)
- }
-}
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt b/plugins/all-modules-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt
deleted file mode 100644
index 9b5251ac..00000000
--- a/plugins/all-modules-page/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.jetbrains.dokka.allModulesPage.templates
-
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import org.jetbrains.dokka.plugability.DokkaContext
-import java.io.File
-import java.nio.file.Files
-
-class FallbackTemplateProcessingStrategy(dokkaContext: DokkaContext) : TemplateProcessingStrategy {
-
- override suspend fun process(input: File, output: File): Boolean = coroutineScope {
- launch(IO) {
- Files.copy(input.toPath(), output.toPath())
- }
- true
- }
-} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkCommandResolutionTest.kt b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt
index 1b4e8638..cbf254a0 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkCommandResolutionTest.kt
+++ b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt
@@ -12,13 +12,14 @@ import org.jetbrains.dokka.links.DRI
import org.junit.Rule
import org.junit.jupiter.api.Test
import org.junit.rules.TemporaryFolder
+import utils.assertHtmlEqualsIgnoringWhitespace
import java.io.File
class ResolveLinkCommandResolutionTest : MultiModuleAbstractTest() {
@get:Rule
val folder: TemporaryFolder = TemporaryFolder()
- fun configuration() = dokkaConfiguration {
+ private fun configuration() = dokkaConfiguration {
modules = listOf(
DokkaModuleDescriptionImpl(
name = "module1",
@@ -49,7 +50,7 @@ class ResolveLinkCommandResolutionTest : MultiModuleAbstractTest() {
}
val expected = createHTML().a {
- href = "../../module2/module2/package2/-sample/index.html"
+ href = "../../module2/package2/-sample/index.html"
span {
+"Sample"
}
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkGfmCommandResolutionTest.kt b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt
index 62aa9338..75576727 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/ResolveLinkGfmCommandResolutionTest.kt
+++ b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt
@@ -18,7 +18,7 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() {
@get:Rule
val folder: TemporaryFolder = TemporaryFolder()
- fun configuration() = dokkaConfiguration {
+ private fun configuration() = dokkaConfiguration {
modules = listOf(
DokkaModuleDescriptionImpl(
name = "module1",
@@ -49,7 +49,7 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() {
}
}.toString()
- val expected = "[Sample text inside](../../module2/module2/package2/-sample/index.md)"
+ val expected = "[Sample text inside](../../module2/package2/-sample/index.md)"
val content = setup(link)
val configuration = configuration()
@@ -61,7 +61,7 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() {
}
}
- fun setup(content: String): File {
+ private fun setup(content: String): File {
folder.create()
val innerModule1 = folder.newFolder("module1", "module1")
val innerModule2 = folder.newFolder("module2", "module2")
@@ -71,4 +71,4 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() {
contentFile.writeText(content)
return contentFile
}
-} \ No newline at end of file
+}
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/mockedPackageListFactory.kt b/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt
index 7a10041b..7a10041b 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/mockedPackageListFactory.kt
+++ b/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt
diff --git a/plugins/base/base-test-utils/build.gradle.kts b/plugins/base/base-test-utils/build.gradle.kts
index 1de0ac0f..5515c965 100644
--- a/plugins/base/base-test-utils/build.gradle.kts
+++ b/plugins/base/base-test-utils/build.gradle.kts
@@ -2,4 +2,5 @@ dependencies {
compileOnly(project(":plugins:base"))
implementation(project(":core:test-api"))
implementation("org.jsoup:jsoup:1.12.1")
+ implementation(kotlin("test-junit"))
}
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/assertHtmlEqualsIgnoringWhitespace.kt b/plugins/base/base-test-utils/src/main/kotlin/utils/assertHtmlEqualsIgnoringWhitespace.kt
index 5a9ff531..f8ef8a41 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/assertHtmlEqualsIgnoringWhitespace.kt
+++ b/plugins/base/base-test-utils/src/main/kotlin/utils/assertHtmlEqualsIgnoringWhitespace.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package utils
import junit.framework.Assert.assertEquals
import org.jsoup.Jsoup
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 1584df02..5c877f03 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -11,6 +11,7 @@ import org.jetbrains.dokka.base.renderers.isImage
import org.jetbrains.dokka.base.renderers.pageId
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider
+import org.jetbrains.dokka.base.templating.InsertTemplateExtra
import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand
import org.jetbrains.dokka.base.templating.ResolveLinkCommand
import org.jetbrains.dokka.links.DRI
@@ -107,6 +108,7 @@ open class HtmlRenderer(
node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() }
node.isAnchorable -> buildAnchor(node.anchor!!, node.anchorLabel!!, node.sourceSetsFilters) { childrenCallback() }
+ node.extra[InsertTemplateExtra] != null -> node.extra[InsertTemplateExtra]?.let { templateCommand(it.command) } ?: Unit
else -> childrenCallback()
}
}
@@ -800,11 +802,7 @@ open class HtmlRenderer(
}
}
} else a {
- href = pathToRoot.split("/")
- .filter { it.isNotBlank() }
- .drop(1).takeIf { it.isNotEmpty() }
- ?.joinToString(separator = "/", postfix = "/index.html")
- ?: "index.html"
+ href = pathToRoot + "index.html"
div {
id = "logo"
}
diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt
index c10029bc..5488f754 100644
--- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt
+++ b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt
@@ -25,7 +25,7 @@ open class DokkaLocationProvider(
page.children.forEach { registerPath(it, prefix) }
} else {
val newPrefix = prefix + page.pathName
- put(page, newPrefix)
+ put(page, if (page is ModulePageNode) prefix else newPrefix)
page.children.forEach { registerPath(it, newPrefix) }
}
diff --git a/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt b/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt
new file mode 100644
index 00000000..b2e883f0
--- /dev/null
+++ b/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.dokka.base.templating
+
+import org.jetbrains.dokka.model.properties.ExtraProperty
+import org.jetbrains.dokka.pages.ContentNode
+
+data class InsertTemplateExtra(val command: Command) : ExtraProperty<ContentNode> {
+
+ companion object : ExtraProperty.Key<ContentNode, InsertTemplateExtra>
+
+ override val key: ExtraProperty.Key<ContentNode, *>
+ get() = Companion
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
index 57345f5c..709274f2 100644
--- a/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
+++ b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
@@ -62,7 +62,7 @@ class ResourceLinksTest : BaseAbstractTest() {
r -> assert(it.`is`("[href=$r], [src=$r]"))
}
relativeResources.forEach {
- r -> assert(it.`is`("[href=../$r] , [src=../$r]"))
+ r -> assert(it.`is`("[href=$r] , [src=$r]"))
}
}
}
diff --git a/plugins/gfm/gfm-template-processing/build.gradle.kts b/plugins/gfm/gfm-template-processing/build.gradle.kts
index f95ef0e4..b401c0c7 100644
--- a/plugins/gfm/gfm-template-processing/build.gradle.kts
+++ b/plugins/gfm/gfm-template-processing/build.gradle.kts
@@ -4,6 +4,7 @@ dependencies {
implementation(project(":plugins:base"))
implementation(project(":plugins:gfm"))
implementation(project(":plugins:all-modules-page"))
+ implementation(project(":plugins:templating"))
val coroutines_version: String by project
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
diff --git a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingPlugin.kt b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingPlugin.kt
index 7df740a5..ba08bcd2 100644
--- a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingPlugin.kt
+++ b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingPlugin.kt
@@ -6,19 +6,21 @@ import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.gfm.GfmPlugin
import org.jetbrains.dokka.gfm.location.MarkdownLocationProvider
import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.templates.TemplatingPlugin
class GfmTemplateProcessingPlugin : DokkaPlugin() {
private val allModulesPagePlugin by lazy { plugin<AllModulesPagePlugin>() }
+ private val templateProcessingPlugin by lazy { plugin<TemplatingPlugin>() }
private val gfmPlugin by lazy { plugin<GfmPlugin>() }
private val dokkaBase by lazy { plugin<DokkaBase>()}
val gfmTemplateProcessingStrategy by extending {
- (allModulesPagePlugin.templateProcessingStrategy
+ (templateProcessingPlugin.templateProcessingStrategy
providing ::GfmTemplateProcessingStrategy
- order { before(allModulesPagePlugin.fallbackProcessingStrategy) })
+ order { before(templateProcessingPlugin.fallbackProcessingStrategy) })
}
val gfmLocationProvider by extending {
diff --git a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt
index 3f2bbd3e..93ce9659 100644
--- a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt
+++ b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt
@@ -1,10 +1,6 @@
package org.jetbrains.dokka.gfm.templateProcessing
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin
-import org.jetbrains.dokka.allModulesPage.templates.TemplateProcessingStrategy
import org.jetbrains.dokka.base.templating.parseJson
import org.jetbrains.dokka.gfm.GfmCommand
import org.jetbrains.dokka.gfm.GfmCommand.Companion.command
@@ -15,6 +11,7 @@ import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.templates.TemplateProcessingStrategy
import java.io.BufferedWriter
import java.io.File
@@ -23,33 +20,30 @@ class GfmTemplateProcessingStrategy(val context: DokkaContext) : TemplateProcess
private val externalModuleLinkResolver =
context.plugin<AllModulesPagePlugin>().querySingle { externalModuleLinkResolver }
- override suspend fun process(input: File, output: File): Boolean = coroutineScope {
+ override fun process(input: File, output: File): Boolean =
if (input.extension == "md") {
- launch(IO) {
- input.bufferedReader().use { reader ->
- //This should also work whenever we have a misconfigured dokka and output is pointing to the input
- //the same way that html processing does
- if (input.absolutePath == output.absolutePath) {
- context.logger.info("Attempting to process GFM templates in place for directory $input, this suggests miss configuration.")
- val lines = reader.readLines()
- output.bufferedWriter().use { writer ->
- lines.forEach { line ->
- writer.processAndWrite(line, output)
- }
-
+ input.bufferedReader().use { reader ->
+ //This should also work whenever we have a misconfigured dokka and output is pointing to the input
+ //the same way that html processing does
+ if (input.absolutePath == output.absolutePath) {
+ context.logger.info("Attempting to process GFM templates in place for directory $input, this suggests miss configuration.")
+ val lines = reader.readLines()
+ output.bufferedWriter().use { writer ->
+ lines.forEach { line ->
+ writer.processAndWrite(line, output)
}
- } else {
- output.bufferedWriter().use { writer ->
- reader.lineSequence().forEach { line ->
- writer.processAndWrite(line, output)
- }
+
+ }
+ } else {
+ output.bufferedWriter().use { writer ->
+ reader.lineSequence().forEach { line ->
+ writer.processAndWrite(line, output)
}
}
}
}
true
} else false
- }
private fun BufferedWriter.processAndWrite(line: String, output: File) =
processLine(line, output).run {
diff --git a/plugins/templating/build.gradle.kts b/plugins/templating/build.gradle.kts
new file mode 100644
index 00000000..6c160a9f
--- /dev/null
+++ b/plugins/templating/build.gradle.kts
@@ -0,0 +1,18 @@
+import org.jetbrains.registerDokkaArtifactPublication
+
+registerDokkaArtifactPublication("templating-plugin") {
+ artifactId = "templating-plugin"
+}
+
+dependencies {
+ implementation(project(":plugins:base"))
+
+ val coroutines_version: String by project
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
+ implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1")
+ val kotlinx_html_version: String by project
+ implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:$kotlinx_html_version")
+
+ implementation("org.jsoup:jsoup:1.12.1")
+ testImplementation(project(":plugins:base:base-test-utils"))
+} \ No newline at end of file
diff --git a/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt b/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt
new file mode 100644
index 00000000..3e7e1290
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt
@@ -0,0 +1,56 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.base.templating.AddToNavigationCommand
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jsoup.nodes.Attributes
+import org.jsoup.nodes.Element
+import org.jsoup.parser.Tag
+import java.io.File
+import java.nio.file.Files
+import java.util.concurrent.ConcurrentHashMap
+
+class AddToNavigationCommandHandler(val context: DokkaContext) : CommandHandler {
+ private val navigationFragments = ConcurrentHashMap<String, Element>()
+
+ override fun handleCommand(element: Element, command: Command, input: File, output: File) {
+ command as AddToNavigationCommand
+ context.configuration.modules.find { it.name == command.moduleName }
+ ?.relativePathToOutputDirectory
+ ?.relativeToOrSelf(context.configuration.outputDir)
+ ?.let { key -> navigationFragments[key.toString()] = element }
+ }
+
+ override fun canHandle(command: Command) = command is AddToNavigationCommand
+
+ override fun finish(output: File) {
+ if (navigationFragments.isNotEmpty()) {
+ val attributes = Attributes().apply {
+ put("class", "sideMenu")
+ }
+ val node = Element(Tag.valueOf("div"), "", attributes)
+ navigationFragments.entries.sortedBy { it.key }.forEach { (moduleName, command) ->
+ command.select("a").forEach { a ->
+ a.attr("href")?.also { a.attr("href", "${moduleName}/${it}") }
+ }
+ command.childNodes().toList().forEachIndexed { index, child ->
+ if (index == 0) {
+ child.attr("id", "$moduleName-nav-submenu")
+ }
+ node.appendChild(child)
+ }
+ }
+
+ Files.write(output.resolve("navigation.html").toPath(), listOf(node.outerHtml()))
+ node.select("a").forEach { a ->
+ a.attr("href")?.also { a.attr("href", "../${it}") }
+ }
+ navigationFragments.keys.forEach {
+ Files.write(
+ output.resolve(it).resolve("navigation.html").toPath(),
+ listOf(node.outerHtml())
+ )
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/templating/src/main/kotlin/templates/CommandHandler.kt b/plugins/templating/src/main/kotlin/templates/CommandHandler.kt
new file mode 100644
index 00000000..d72092a1
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/CommandHandler.kt
@@ -0,0 +1,11 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.base.templating.Command
+import org.jsoup.nodes.Element
+import java.io.File
+
+interface CommandHandler {
+ fun handleCommand(element: Element, command: Command, input: File, output: File)
+ fun canHandle(command: Command): Boolean
+ fun finish(output: File) {}
+} \ No newline at end of file
diff --git a/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
new file mode 100644
index 00000000..c3b9aa53
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt
@@ -0,0 +1,41 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.base.templating.parseJson
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import java.io.File
+import java.nio.file.Files
+
+class DirectiveBasedHtmlTemplateProcessingStrategy(private val context: DokkaContext) : TemplateProcessingStrategy {
+
+ private val directiveBasedCommandHandlers =
+ context.plugin<TemplatingPlugin>().query { directiveBasedCommandHandlers }
+
+ override fun process(input: File, output: File): Boolean =
+ if (input.isFile && input.extension == "html") {
+ val document = Jsoup.parse(input, "UTF-8")
+ document.outputSettings().indentAmount(0).prettyPrint(false)
+ document.select("dokka-template-command").forEach {
+ handleCommand(it, parseJson(it.attr("data")), input, output)
+ }
+ Files.write(output.toPath(), listOf(document.outerHtml()))
+ true
+ } else false
+
+ fun handleCommand(element: Element, command: Command, input: File, output: File) {
+ val handlers = directiveBasedCommandHandlers.filter { it.canHandle(command) }
+ if (handlers.isEmpty())
+ context.logger.warn("Unknown templating command $command")
+ else
+ handlers.forEach { it.handleCommand(element, command, input, output) }
+
+ }
+
+ override fun finish(output: File) {
+ directiveBasedCommandHandlers.forEach { it.finish(output) }
+ }
+}
diff --git a/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt
new file mode 100644
index 00000000..4e88c318
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt
@@ -0,0 +1,13 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.plugability.DokkaContext
+import java.io.File
+import java.nio.file.Files
+
+class FallbackTemplateProcessingStrategy(dokkaContext: DokkaContext) : TemplateProcessingStrategy {
+
+ override fun process(input: File, output: File): Boolean {
+ if(input != output) input.copyTo(output, overwrite = true)
+ return true
+ }
+} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt
index c6c67752..a2d55209 100644
--- a/plugins/all-modules-page/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt
+++ b/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt
@@ -1,16 +1,12 @@
package org.jetbrains.dokka.allModulesPage.templates
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
-import java.io.File
-import java.nio.file.Files
-import com.fasterxml.jackson.module.kotlin.treeToValue
import org.jetbrains.dokka.base.renderers.html.SearchRecord
-import org.jetbrains.dokka.base.templating.*
+import org.jetbrains.dokka.base.templating.AddToSearch
+import org.jetbrains.dokka.base.templating.parseJson
+import org.jetbrains.dokka.base.templating.toJsonString
import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.templates.TemplateProcessingStrategy
+import java.io.File
import java.util.concurrent.ConcurrentHashMap
abstract class BaseJsonNavigationTemplateProcessingStrategy(val context: DokkaContext) : TemplateProcessingStrategy {
@@ -22,38 +18,36 @@ abstract class BaseJsonNavigationTemplateProcessingStrategy(val context: DokkaCo
open fun canProcess(file: File): Boolean =
file.extension == "json" && file.nameWithoutExtension == navigationFileNameWithoutExtension
- override suspend fun process(input: File, output: File): Boolean = coroutineScope {
+ override fun process(input: File, output: File): Boolean {
val canProcess = canProcess(input)
if (canProcess) {
- launch {
- withContext(Dispatchers.IO) {
- runCatching { parseJson<AddToSearch>(input.readText()) }.getOrNull()
- }?.let { command ->
- fragments[command.moduleName] = command.elements
- } ?: fallbackToCopy(input, output)
- }
+ runCatching { parseJson<AddToSearch>(input.readText()) }.getOrNull()?.let { command ->
+ context.configuration.modules.find { it.name == command.moduleName }?.relativePathToOutputDirectory
+ ?.relativeToOrSelf(context.configuration.outputDir)
+ ?.let { key ->
+ fragments[key.toString()] = command.elements
+ }
+ } ?: fallbackToCopy(input, output)
}
- canProcess
+ return canProcess
}
- override suspend fun finish(output: File) {
+ override fun finish(output: File) {
if (fragments.isNotEmpty()) {
val content = toJsonString(fragments.entries.flatMap { (moduleName, navigation) ->
navigation.map { it.withResolvedLocation(moduleName) }
})
- withContext(Dispatchers.IO) {
- output.resolve("$path/$navigationFileNameWithoutExtension.json").writeText(content)
+ output.resolve("$path/$navigationFileNameWithoutExtension.json").writeText(content)
- fragments.keys.forEach {
- output.resolve(it).resolve("$path/$navigationFileNameWithoutExtension.json").writeText(content)
- }
+ fragments.keys.forEach {
+ output.resolve(it).resolve("$path/$navigationFileNameWithoutExtension.json").writeText(content)
}
}
}
- private suspend fun fallbackToCopy(input: File, output: File) {
+ private fun fallbackToCopy(input: File, output: File) {
context.logger.warn("Falling back to just copying file for ${input.name} even thought it should process it")
- withContext(Dispatchers.IO) { input.copyTo(output) }
+ input.copyTo(output)
}
private fun SearchRecord.withResolvedLocation(moduleName: String): SearchRecord =
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/PathToRootSubstitutor.kt b/plugins/templating/src/main/kotlin/templates/PathToRootSubstitutor.kt
index 5056b724..da81432e 100644
--- a/plugins/all-modules-page/src/main/kotlin/templates/PathToRootSubstitutor.kt
+++ b/plugins/templating/src/main/kotlin/templates/PathToRootSubstitutor.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand
import org.jetbrains.dokka.base.templating.SubstitutionCommand
@@ -6,9 +6,9 @@ import org.jetbrains.dokka.plugability.DokkaContext
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().split(File.separator).joinToString(separator = "/", postfix = "/") { it }
} else null
-
} \ No newline at end of file
diff --git a/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt b/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt
new file mode 100644
index 00000000..c7b15137
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt
@@ -0,0 +1,60 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.base.templating.SubstitutionCommand
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import org.jsoup.nodes.DataNode
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.Node
+import org.jsoup.nodes.TextNode
+import java.io.File
+
+class SubstitutionCommandHandler(context: DokkaContext) : CommandHandler {
+
+ override fun handleCommand(element: Element, command: Command, input: File, output: File) {
+ command as SubstitutionCommand
+ substitute(element, TemplatingContext(input, output, element, command))
+ }
+
+ override fun canHandle(command: Command): Boolean = command is SubstitutionCommand
+
+ private val substitutors = context.plugin<TemplatingPlugin>().query { substitutor }
+
+ private fun findSubstitution(commandContext: TemplatingContext<SubstitutionCommand>, match: MatchResult): String =
+ substitutors.asSequence().mapNotNull { it.trySubstitute(commandContext, match) }.firstOrNull() ?: match.value
+
+ private fun substitute(element: Element, commandContext: TemplatingContext<SubstitutionCommand>) {
+ val regex = commandContext.command.pattern.toRegex()
+ element.children().forEach { it.traverseToSubstitute(regex, commandContext) }
+
+ val childrenCopy = element.children().toList()
+ val position = element.elementSiblingIndex()
+ val parent = element.parent()
+ element.remove()
+
+ parent.insertChildren(position, childrenCopy)
+ }
+
+ private fun Node.traverseToSubstitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) {
+ when (this) {
+ is TextNode -> replaceWith(TextNode(wholeText.substitute(regex, commandContext)))
+ is DataNode -> replaceWith(DataNode(wholeData.substitute(regex, commandContext)))
+ is Element -> {
+ attributes().forEach { attr(it.key, it.value.substitute(regex, commandContext)) }
+ childNodes().forEach { it.traverseToSubstitute(regex, commandContext) }
+ }
+ }
+ }
+
+ private fun String.substitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) = buildString {
+ var lastOffset = 0
+ regex.findAll(this@substitute).forEach { match ->
+ append(this@substitute, lastOffset, match.range.first)
+ append(findSubstitution(commandContext, match))
+ lastOffset = match.range.last + 1
+ }
+ append(this@substitute, lastOffset, this@substitute.length)
+ }
+} \ No newline at end of file
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/Substitutor.kt b/plugins/templating/src/main/kotlin/templates/Substitutor.kt
index 98f1d88e..55463974 100644
--- a/plugins/all-modules-page/src/main/kotlin/templates/Substitutor.kt
+++ b/plugins/templating/src/main/kotlin/templates/Substitutor.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import org.jetbrains.dokka.base.templating.SubstitutionCommand
diff --git a/plugins/all-modules-page/src/main/kotlin/templates/TemplateProcessor.kt b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt
index 18d63df0..8fbd76b6 100644
--- a/plugins/all-modules-page/src/main/kotlin/templates/TemplateProcessor.kt
+++ b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt
@@ -1,31 +1,27 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import kotlinx.coroutines.*
-import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin
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
import org.jsoup.nodes.Element
import java.io.File
-import java.nio.file.Files
-import java.nio.file.Path
-import kotlin.coroutines.coroutineContext
interface TemplateProcessor {
fun process()
}
interface TemplateProcessingStrategy {
- suspend fun process(input: File, output: File): Boolean
- suspend fun finish(output: File) {}
+ fun process(input: File, output: File): Boolean
+ fun finish(output: File) {}
}
class DefaultTemplateProcessor(
private val context: DokkaContext,
): TemplateProcessor {
- private val strategies: List<TemplateProcessingStrategy> = context.plugin<AllModulesPagePlugin>().query { templateProcessingStrategy }
+ private val strategies: List<TemplateProcessingStrategy> = context.plugin<TemplatingPlugin>().query { templateProcessingStrategy }
override fun process() = runBlocking(Dispatchers.Default) {
coroutineScope {
@@ -47,7 +43,7 @@ class DefaultTemplateProcessor(
launch { source.resolve(it).visit(target.resolve(it)) }
}
} else {
- strategies.asSequence().first { it.process(source, target) }
+ strategies.first { it.process(source, target) }
}
}
}
diff --git a/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt
new file mode 100644
index 00000000..29ca4904
--- /dev/null
+++ b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt
@@ -0,0 +1,50 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.allModulesPage.templates.NavigationSearchTemplateStrategy
+import org.jetbrains.dokka.allModulesPage.templates.PagesSearchTemplateStrategy
+import org.jetbrains.dokka.plugability.DokkaPlugin
+
+class TemplatingPlugin : DokkaPlugin() {
+
+ val templateProcessor by extensionPoint<TemplateProcessor>()
+ val templateProcessingStrategy by extensionPoint<TemplateProcessingStrategy>()
+ val directiveBasedCommandHandlers by extensionPoint<CommandHandler>()
+
+ val substitutor by extensionPoint<Substitutor>()
+
+ val defaultTemplateProcessor by extending {
+ templateProcessor providing ::DefaultTemplateProcessor
+ }
+
+ val directiveBasedHtmlTemplateProcessingStrategy by extending {
+ templateProcessingStrategy providing ::DirectiveBasedHtmlTemplateProcessingStrategy order {
+ before(fallbackProcessingStrategy)
+ }
+ }
+ val navigationSearchTemplateStrategy by extending {
+ templateProcessingStrategy providing ::NavigationSearchTemplateStrategy order {
+ before(fallbackProcessingStrategy)
+ }
+ }
+
+ val pagesSearchTemplateStrategy by extending {
+ templateProcessingStrategy providing ::PagesSearchTemplateStrategy order {
+ before(fallbackProcessingStrategy)
+ }
+ }
+
+ val fallbackProcessingStrategy by extending {
+ templateProcessingStrategy providing ::FallbackTemplateProcessingStrategy
+ }
+
+ val pathToRootSubstitutor by extending {
+ substitutor providing ::PathToRootSubstitutor
+ }
+
+ val addToNavigationCommandHandler by extending {
+ directiveBasedCommandHandlers providing ::AddToNavigationCommandHandler
+ }
+ val substitutionCommandHandler by extending {
+ directiveBasedCommandHandlers providing ::SubstitutionCommandHandler
+ }
+} \ No newline at end of file
diff --git a/plugins/templating/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/templating/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..38e2d1bf
--- /dev/null
+++ b/plugins/templating/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1 @@
+org.jetbrains.dokka.templates.TemplatingPlugin \ No newline at end of file
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToNavigationCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt
index f917916a..19c940c2 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToNavigationCommandResolutionTest.kt
+++ b/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import kotlinx.html.a
import kotlinx.html.div
@@ -6,7 +6,6 @@ import kotlinx.html.id
import kotlinx.html.span
import kotlinx.html.stream.createHTML
import org.jetbrains.dokka.DokkaModuleDescriptionImpl
-import org.jetbrains.dokka.allModulesPage.MultiModuleAbstractTest
import org.jetbrains.dokka.base.renderers.html.templateCommand
import org.jetbrains.dokka.base.templating.AddToNavigationCommand
import org.jetbrains.dokka.plugability.DokkaContext
@@ -15,8 +14,9 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.junit.rules.TemporaryFolder
+import utils.assertHtmlEqualsIgnoringWhitespace
-class AddToNavigationCommandResolutionTest : MultiModuleAbstractTest() {
+class AddToNavigationCommandResolutionTest : TemplatingAbstractTest() {
@get:Rule
val folder: TemporaryFolder = TemporaryFolder()
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToSearchCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt
index 238134c7..a962d524 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/AddToSearchCommandResolutionTest.kt
+++ b/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt
@@ -1,7 +1,6 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import org.jetbrains.dokka.DokkaModuleDescriptionImpl
-import org.jetbrains.dokka.allModulesPage.MultiModuleAbstractTest
import org.jetbrains.dokka.base.renderers.html.SearchRecord
import org.jetbrains.dokka.base.templating.AddToSearch
import org.jetbrains.dokka.base.templating.parseJson
@@ -13,7 +12,7 @@ import org.junit.rules.TemporaryFolder
import java.io.File
import kotlin.test.assertEquals
-class AddToSearchCommandResolutionTest : MultiModuleAbstractTest() {
+class AddToSearchCommandResolutionTest : TemplatingAbstractTest() {
companion object {
val elements = listOf(
SearchRecord(name = "name1", location = "location1"),
diff --git a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/SubstitutionCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt
index 89984b46..d7143f11 100644
--- a/plugins/all-modules-page/src/test/kotlin/org/jetbrains/dokka/allModulesPage/templates/SubstitutionCommandResolutionTest.kt
+++ b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt
@@ -1,19 +1,19 @@
-package org.jetbrains.dokka.allModulesPage.templates
+package org.jetbrains.dokka.templates
import kotlinx.html.a
import kotlinx.html.div
import kotlinx.html.id
import kotlinx.html.stream.createHTML
import org.jetbrains.dokka.DokkaModuleDescriptionImpl
-import org.jetbrains.dokka.allModulesPage.MultiModuleAbstractTest
import org.jetbrains.dokka.base.renderers.html.templateCommand
import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import org.junit.jupiter.api.Test
+import utils.assertHtmlEqualsIgnoringWhitespace
import java.io.File
-class SubstitutionCommandResolutionTest : MultiModuleAbstractTest() {
+class SubstitutionCommandResolutionTest : TemplatingAbstractTest() {
@get:Rule
val folder: TemporaryFolder = TemporaryFolder()
diff --git a/plugins/templating/src/test/kotlin/templates/TemplatingDokkaTestGenerator.kt b/plugins/templating/src/test/kotlin/templates/TemplatingDokkaTestGenerator.kt
new file mode 100644
index 00000000..906fb1ea
--- /dev/null
+++ b/plugins/templating/src/test/kotlin/templates/TemplatingDokkaTestGenerator.kt
@@ -0,0 +1,63 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaGenerator
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.testApi.logger.TestLogger
+import org.jetbrains.dokka.testApi.testRunner.AbstractTest
+import org.jetbrains.dokka.testApi.testRunner.DokkaTestGenerator
+import org.jetbrains.dokka.testApi.testRunner.TestBuilder
+import org.jetbrains.dokka.testApi.testRunner.TestMethods
+import org.jetbrains.dokka.utilities.DokkaConsoleLogger
+import org.jetbrains.dokka.utilities.DokkaLogger
+
+class TemplatingDokkaTestGenerator(
+ configuration: DokkaConfiguration,
+ logger: DokkaLogger,
+ testMethods: TemplatingTestMethods,
+ additionalPlugins: List<DokkaPlugin> = emptyList()
+) : DokkaTestGenerator<TemplatingTestMethods>(
+ configuration,
+ logger,
+ testMethods,
+ additionalPlugins + TemplatingPlugin() + TestTemplatingPlugin()
+) {
+ override fun generate() = with(testMethods) {
+ val dokkaGenerator = DokkaGenerator(configuration, logger)
+
+ val context =
+ dokkaGenerator.initializePlugins(configuration, logger, additionalPlugins)
+
+ pluginsSetupStage(context)
+
+ val generation = context.single(CoreExtensions.generation) as TestTemplatingGeneration
+
+ generation.processSubmodules()
+ submoduleProcessingStage(context)
+ }
+
+}
+
+open class TemplatingTestMethods(
+ open val pluginsSetupStage: (DokkaContext) -> Unit,
+ open val submoduleProcessingStage: (DokkaContext) -> Unit,
+) : TestMethods
+
+class TemplatingTestBuilder : TestBuilder<TemplatingTestMethods>() {
+ var pluginsSetupStage: (DokkaContext) -> Unit = {}
+ var submoduleProcessingStage: (DokkaContext) -> Unit = {}
+
+ override fun build() = TemplatingTestMethods(
+ pluginsSetupStage,
+ submoduleProcessingStage,
+ )
+}
+
+abstract class TemplatingAbstractTest(logger: TestLogger = TestLogger(DokkaConsoleLogger)) :
+ AbstractTest<TemplatingTestMethods, TemplatingTestBuilder, TemplatingDokkaTestGenerator>(
+ ::TemplatingTestBuilder,
+ ::TemplatingDokkaTestGenerator,
+ logger,
+ )
diff --git a/plugins/templating/src/test/kotlin/templates/TestTemplatingGeneration.kt b/plugins/templating/src/test/kotlin/templates/TestTemplatingGeneration.kt
new file mode 100644
index 00000000..0a5bae4b
--- /dev/null
+++ b/plugins/templating/src/test/kotlin/templates/TestTemplatingGeneration.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.Timer
+import org.jetbrains.dokka.generation.Generation
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+
+class TestTemplatingGeneration(context: DokkaContext): Generation {
+
+ val templatingPlugin by lazy { context.plugin<TemplatingPlugin>() }
+
+ override fun Timer.generate() {
+ report("Processing submodules")
+ processSubmodules()
+ }
+
+ fun processSubmodules() =
+ templatingPlugin.querySingle { templateProcessor }.process()
+
+ override val generationName = "test template generation"
+} \ No newline at end of file
diff --git a/plugins/templating/src/test/kotlin/templates/TestTemplatingPlugin.kt b/plugins/templating/src/test/kotlin/templates/TestTemplatingPlugin.kt
new file mode 100644
index 00000000..1ed961b8
--- /dev/null
+++ b/plugins/templating/src/test/kotlin/templates/TestTemplatingPlugin.kt
@@ -0,0 +1,16 @@
+package org.jetbrains.dokka.templates
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.plugability.DokkaPlugin
+
+class TestTemplatingPlugin: DokkaPlugin() {
+
+ val dokkaBase by lazy { plugin<DokkaBase>() }
+
+ val allModulesPageGeneration by extending {
+ (CoreExtensions.generation
+ providing ::TestTemplatingGeneration
+ override dokkaBase.singleGeneration)
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/build.gradle.kts b/plugins/versioning/build.gradle.kts
new file mode 100644
index 00000000..39cc0bdf
--- /dev/null
+++ b/plugins/versioning/build.gradle.kts
@@ -0,0 +1,19 @@
+import org.jetbrains.registerDokkaArtifactPublication
+
+registerDokkaArtifactPublication("versioning-plugin") {
+ artifactId = "versioning-plugin"
+}
+
+dependencies {
+ implementation(project(":plugins:base"))
+ implementation(project(":plugins:templating"))
+
+ val coroutines_version: String by project
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
+ implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1")
+ val kotlinx_html_version: String by project
+ implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:$kotlinx_html_version")
+
+ implementation("org.jsoup:jsoup:1.12.1")
+ implementation("org.apache.maven:maven-artifact:3.6.3")
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionCommandConsumer.kt b/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionCommandConsumer.kt
new file mode 100644
index 00000000..2577b2da
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionCommandConsumer.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.versioning
+
+import kotlinx.html.unsafe
+import kotlinx.html.visit
+import kotlinx.html.visitAndFinalize
+import org.jetbrains.dokka.base.renderers.html.TemplateBlock
+import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer
+import org.jetbrains.dokka.base.renderers.html.templateCommandFor
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+
+class ReplaceVersionCommandConsumer(context: DokkaContext) : ImmediateHtmlCommandConsumer {
+
+ private val versionsNavigationCreator =
+ context.plugin<VersioningPlugin>().querySingle { versionsNavigationCreator }
+
+ override fun canProcess(command: Command) = command is ReplaceVersionsCommand
+
+ override fun <R> processCommand(
+ command: Command,
+ block: TemplateBlock,
+ tagConsumer: ImmediateResolutionTagConsumer<R>
+ ) {
+ command as ReplaceVersionsCommand
+ templateCommandFor(command, tagConsumer).visit {
+ unsafe {
+ +versionsNavigationCreator()
+ }
+ }
+ }
+
+ override fun <R> processCommandAndFinalize(
+ command: Command,
+ block: TemplateBlock,
+ tagConsumer: ImmediateResolutionTagConsumer<R>
+ ): R =
+ templateCommandFor(command, tagConsumer).visitAndFinalize(tagConsumer) {
+ unsafe {
+ +versionsNavigationCreator()
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionsCommand.kt b/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionsCommand.kt
new file mode 100644
index 00000000..2d2c8e36
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/ReplaceVersionsCommand.kt
@@ -0,0 +1,26 @@
+package org.jetbrains.dokka.versioning
+
+
+import org.jetbrains.dokka.base.templating.Command
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.templates.CommandHandler
+import org.jsoup.nodes.Element
+import java.io.File
+
+object ReplaceVersionsCommand : Command
+
+class ReplaceVersionCommandHandler(context: DokkaContext) : CommandHandler {
+
+ val versionsNavigationCreator by lazy {
+ context.plugin<VersioningPlugin>().querySingle { versionsNavigationCreator }
+ }
+
+ override fun canHandle(command: Command): Boolean = command is ReplaceVersionsCommand
+
+ override fun handleCommand(element: Element, command: Command, input: File, output: File) {
+ element.empty()
+ element.append(versionsNavigationCreator(output))
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/VersioningConfiguration.kt b/plugins/versioning/src/main/kotlin/versioning/VersioningConfiguration.kt
new file mode 100644
index 00000000..98eef33d
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/VersioningConfiguration.kt
@@ -0,0 +1,20 @@
+package org.jetbrains.dokka.versioning
+
+import org.jetbrains.dokka.plugability.ConfigurableBlock
+import org.jetbrains.dokka.plugability.DokkaContext
+import java.io.File
+
+data class VersioningConfiguration(
+ var olderVersionsDir: File? = defaultOlderVersionsDir,
+ var versionsOrdering: List<String>? = defaultVersionsOrdering,
+ var version: String? = defaultVersion,
+) : ConfigurableBlock {
+ fun versionFromConfigurationOrModule(dokkaContext: DokkaContext): String =
+ version ?: dokkaContext.configuration.moduleVersion ?: "1.0"
+
+ companion object {
+ val defaultOlderVersionsDir: File? = null
+ val defaultVersionsOrdering: List<String>? = null
+ val defaultVersion = null
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt b/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt
new file mode 100644
index 00000000..699c87e6
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt
@@ -0,0 +1,101 @@
+package org.jetbrains.dokka.versioning
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
+import kotlinx.coroutines.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.configuration
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import org.jetbrains.dokka.templates.TemplateProcessingStrategy
+import org.jetbrains.dokka.templates.TemplatingPlugin
+import java.io.File
+
+interface VersioningHandler : () -> Unit {
+ fun getVersions(): Map<VersionId, File>
+ fun currentVersion(): File?
+}
+
+typealias VersionId = String
+
+class DefaultVersioningHandler(val context: DokkaContext) : VersioningHandler {
+
+ private val mapper = ObjectMapper()
+
+ private lateinit var versions: Map<VersionId, File>
+
+ private val processingStrategies: List<TemplateProcessingStrategy> =
+ context.plugin<TemplatingPlugin>().query { templateProcessingStrategy }
+
+ private val configuration = configuration<VersioningPlugin, VersioningConfiguration>(context)
+
+ override fun getVersions() = versions
+
+ override fun currentVersion() = configuration?.let { versionsConfiguration ->
+ versions[versionsConfiguration.versionFromConfigurationOrModule(context)]
+ }
+
+ override fun invoke() {
+ configuration?.let { versionsConfiguration ->
+ versions =
+ mapOf(versionsConfiguration.versionFromConfigurationOrModule(context) to context.configuration.outputDir)
+ versionsConfiguration.olderVersionsDir?.let {
+ handlePreviousVersions(it, context.configuration.outputDir)
+ }
+ mapper.writeValue(
+ context.configuration.outputDir.resolve(VERSIONS_FILE),
+ Version(versionsConfiguration.versionFromConfigurationOrModule(context))
+ )
+ }
+ }
+
+ private fun handlePreviousVersions(olderVersionDir: File, output: File): Map<String, File> {
+ assert(olderVersionDir.isDirectory) { "Supplied previous version $olderVersionDir is not a directory!" }
+ return versionsWithOriginDir(olderVersionDir)
+ .also { fetched ->
+ versions = versions + fetched.map { (key, _) ->
+ key to output.resolve(OLDER_VERSIONS_DIR).resolve(key)
+ }.toMap()
+ }
+ .onEach { (version, path) -> copyVersion(version, path, output) }.toMap()
+ }
+
+ private fun versionsWithOriginDir(olderVersionRootDir: File) =
+ olderVersionRootDir.listFiles().orEmpty().mapNotNull { versionDir ->
+ versionDir.listFiles { _, name -> name == VERSIONS_FILE }?.firstOrNull()?.let { file ->
+ val versionsContent = mapper.readValue<Version>(file)
+ Pair(versionsContent.version, versionDir)
+ }.also {
+ if (it == null) context.logger.warn("Failed to find versions file named $VERSIONS_FILE in $versionDir")
+ }
+ }
+
+ private fun copyVersion(version: VersionId, versionRoot: File, output: File) {
+ val targetParent = output.resolve(OLDER_VERSIONS_DIR).resolve(version).apply { mkdirs() }
+ runBlocking(Dispatchers.Default) {
+ coroutineScope {
+ versionRoot.listFiles().orEmpty().forEach { versionRootContent ->
+ launch {
+ if (versionRootContent.isDirectory) versionRootContent.copyRecursively(
+ targetParent.resolve(versionRootContent.name),
+ overwrite = true
+ )
+ else processingStrategies.first {
+ it.process(versionRootContent, targetParent.resolve(versionRootContent.name))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private data class Version(
+ @JsonProperty("version") val version: String,
+ )
+
+ companion object {
+ private const val OLDER_VERSIONS_DIR = "older"
+ private const val VERSIONS_FILE = "version.json"
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/VersioningPlugin.kt b/plugins/versioning/src/main/kotlin/versioning/VersioningPlugin.kt
new file mode 100644
index 00000000..9c20a128
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/VersioningPlugin.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.versioning
+
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.plugability.configuration
+import org.jetbrains.dokka.templates.TemplatingPlugin
+
+class VersioningPlugin : DokkaPlugin() {
+
+ val versioningHandler by extensionPoint<VersioningHandler>()
+ val versionsNavigationCreator by extensionPoint<VersionsNavigationCreator>()
+ val versionsOrdering by extensionPoint<VersionsOrdering>()
+
+ private val dokkaBase by lazy { plugin<DokkaBase>() }
+ private val templatingPlugin by lazy { plugin<TemplatingPlugin>() }
+
+ val defaultVersioningHandler by extending {
+ versioningHandler providing ::DefaultVersioningHandler
+ }
+ val defaultVersioningNavigationCreator by extending {
+ versionsNavigationCreator providing ::HtmlVersionsNavigationCreator
+ }
+ val replaceVersionCommandHandler by extending {
+ templatingPlugin.directiveBasedCommandHandlers providing ::ReplaceVersionCommandHandler
+ }
+ val resolveLinkConsumer by extending {
+ dokkaBase.immediateHtmlCommandConsumer providing ::ReplaceVersionCommandConsumer
+ }
+ val cssStyleInstaller by extending {
+ dokkaBase.htmlPreprocessors with MultiModuleStylesInstaller order { after(dokkaBase.assetsInstaller) }
+ }
+ val versionsDefaultOrdering by extending {
+ versionsOrdering providing { ctx ->
+ configuration<VersioningPlugin, VersioningConfiguration>(ctx)?.versionsOrdering?.let {
+ ByConfigurationVersionOrdering(ctx)
+ } ?: SemVerVersionOrdering()
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/VersionsNavigationCreator.kt b/plugins/versioning/src/main/kotlin/versioning/VersionsNavigationCreator.kt
new file mode 100644
index 00000000..76653d47
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/VersionsNavigationCreator.kt
@@ -0,0 +1,53 @@
+package org.jetbrains.dokka.versioning
+
+import kotlinx.html.a
+import kotlinx.html.button
+import kotlinx.html.div
+import kotlinx.html.i
+import kotlinx.html.stream.appendHTML
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import java.io.File
+import java.nio.file.Files.isDirectory
+import java.nio.file.Path
+
+interface VersionsNavigationCreator {
+ operator fun invoke(): String
+ operator fun invoke(output: File): String
+}
+
+class HtmlVersionsNavigationCreator(val context: DokkaContext) : VersionsNavigationCreator {
+
+ private val versioningHandler by lazy { context.plugin<VersioningPlugin>().querySingle { versioningHandler } }
+
+ private val versionsOrdering by lazy { context.plugin<VersioningPlugin>().querySingle { versionsOrdering } }
+
+ override fun invoke(): String =
+ versioningHandler.currentVersion()?.let { invoke(it) }.orEmpty()
+
+ override fun invoke(output: File): String {
+ val position = output.takeIf { it.isDirectory } ?: output.parentFile
+ return versioningHandler.getVersions()
+ .let { versions -> versionsOrdering.order(versions.keys.toList()).map { it to versions[it] } }
+ .takeIf { it.isNotEmpty() }
+ ?.let { versions ->
+ StringBuilder().appendHTML().div(classes = "versions-dropdown") {
+ button(classes = "versions-dropdown-button") {
+ versions.first { (_, versionLocation) -> versionLocation?.absolutePath == position.absolutePath }
+ .let { (version, _) ->
+ text(version)
+ }
+ i(classes = "fa fa-caret-down")
+ }
+ div(classes = "versions-dropdown-data") {
+ versions.forEach { (version, path) ->
+ a(href = path?.resolve("index.html")?.toRelativeString(position)) {
+ text(version)
+ }
+ }
+ }
+ }.toString()
+ }.orEmpty()
+ }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/VersionsOrdering.kt b/plugins/versioning/src/main/kotlin/versioning/VersionsOrdering.kt
new file mode 100644
index 00000000..f72e2df6
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/VersionsOrdering.kt
@@ -0,0 +1,23 @@
+package org.jetbrains.dokka.versioning
+
+import org.apache.maven.artifact.versioning.ComparableVersion
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.configuration
+import org.jetbrains.dokka.versioning.VersionId
+import org.jetbrains.dokka.versioning.VersioningConfiguration
+import org.jetbrains.dokka.versioning.VersioningPlugin
+
+fun interface VersionsOrdering {
+ fun order(records: List<VersionId>): List<VersionId>
+}
+
+class ByConfigurationVersionOrdering(val dokkaContext: DokkaContext) : VersionsOrdering {
+ override fun order(records: List<VersionId>): List<VersionId> =
+ configuration<VersioningPlugin, VersioningConfiguration>(dokkaContext)?.versionsOrdering
+ ?: throw IllegalStateException("Attempted to use a configuration ordering without providing configuration")
+}
+
+class SemVerVersionOrdering : VersionsOrdering {
+ override fun order(records: List<VersionId>): List<VersionId> =
+ records.map { it to ComparableVersion(it) }.sortedByDescending { it.second }.map { it.first }
+} \ No newline at end of file
diff --git a/plugins/versioning/src/main/kotlin/versioning/htmlPreprocessors.kt b/plugins/versioning/src/main/kotlin/versioning/htmlPreprocessors.kt
new file mode 100644
index 00000000..5852ba9e
--- /dev/null
+++ b/plugins/versioning/src/main/kotlin/versioning/htmlPreprocessors.kt
@@ -0,0 +1,24 @@
+package org.jetbrains.dokka.versioning
+
+import org.jetbrains.dokka.pages.RendererSpecificResourcePage
+import org.jetbrains.dokka.pages.RenderingStrategy
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.transformers.pages.PageTransformer
+
+object MultiModuleStylesInstaller : PageTransformer {
+ private val stylesPages = listOf(
+ "styles/multimodule.css",
+ )
+
+ override fun invoke(input: RootPageNode): RootPageNode =
+ input.modified(
+ children = input.children + stylesPages.toRenderSpecificResourcePage()
+ ).transformContentPagesTree {
+ it.modified(
+ embeddedResources = it.embeddedResources + stylesPages
+ )
+ }
+}
+
+private fun List<String>.toRenderSpecificResourcePage(): List<RendererSpecificResourcePage> =
+ map { RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) }
diff --git a/plugins/versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..1b0f1e3e
--- /dev/null
+++ b/plugins/versioning/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1 @@
+org.jetbrains.dokka.versioning.VersioningPlugin \ No newline at end of file
diff --git a/plugins/versioning/src/main/resources/dokka/styles/multimodule.css b/plugins/versioning/src/main/resources/dokka/styles/multimodule.css
new file mode 100644
index 00000000..4334d759
--- /dev/null
+++ b/plugins/versioning/src/main/resources/dokka/styles/multimodule.css
@@ -0,0 +1,37 @@
+.versions-dropdown {
+ float: right;
+}
+
+.versions-dropdown-button {
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+}
+
+.versions-dropdown-button:hover,
+.versions-dropdown-button:focus {
+ background-color: #f1f1f1;
+}
+
+.versions-dropdown-data {
+ display: none;
+ position: absolute;
+ background-color: #f1f1f1;
+ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+ z-index: 1;
+}
+
+.versions-dropdown-data a {
+ padding: 5px;
+ text-decoration: none;
+ display: block;
+ color: black;
+}
+
+.versions-dropdown-data a:hover {
+ background-color: #ddd
+}
+
+.versions-dropdown:hover .versions-dropdown-data {
+ display: block;
+} \ No newline at end of file
diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAutoConfigurationTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAutoConfigurationTest.kt
index 76215762..455a906b 100644
--- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAutoConfigurationTest.kt
+++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAutoConfigurationTest.kt
@@ -69,11 +69,13 @@ class AndroidAutoConfigurationTest {
dokkaTasks.flatMap { it.dokkaSourceSets }.forEach { sourceSet ->
/*
+
There is no better way of checking for empty classpath at the moment (without resolving dependencies).
We assume, that an empty classpath can be resolved
We assume, that a non-empty classpath will not be able to resolve (no repositories defined)
*/
+
assertFailsWith<ResolveException> { sourceSet.classpath.files }
}
}
-}
+} \ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index f9b348d0..b9433391 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -17,6 +17,8 @@ include("plugins:base:frontend")
include("plugins:base:search-component")
include("plugins:base:base-test-utils")
include("plugins:all-modules-page")
+include("plugins:templating")
+include("plugins:versioning")
include("plugins:mathjax")
include("plugins:gfm")