package org.jetbrains.dokka import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.DokkaConfiguration.* import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.utilities.DokkaLogger /** * DokkaGenerator is the main entry point for generating documentation * * [generate] method has been split into submethods for test reasons */ class DokkaGenerator( private val configuration: DokkaConfiguration, private val logger: DokkaLogger ) { fun generate() = timed { report("Initializing plugins") val context = initializePlugins(configuration, logger) report("Creating documentation models") val modulesFromPlatforms = createDocumentationModels(context) report("Transforming documentation model before merging") val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms, context) report("Merging documentation models") val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge, context) report("Transforming documentation model after merging") val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel, context) report("Creating pages") val pages = createPages(transformedDocumentation, context) report("Transforming pages") val transformedPages = transformPages(pages, context) report("Rendering") render(transformedPages, context) reportAfterRendering(context) }.dump("\n\n === TIME MEASUREMENT ===\n") fun generateAllModulesPage() = timed { report("Initializing plugins") val context = initializePlugins(configuration, logger) report("Creating all modules page") val pages = createAllModulePage(context) report("Transforming pages") val transformedPages = transformAllModulesPage(pages, context) report("Rendering") render(transformedPages, context) }.dump("\n\n === TIME MEASUREMENT ===\n") fun initializePlugins( configuration: DokkaConfiguration, logger: DokkaLogger, additionalPlugins: List = emptyList() ) = DokkaContext.create(configuration, logger, additionalPlugins) fun createDocumentationModels( context: DokkaContext ) = context.configuration.sourceSets .flatMap { sourceSet -> translateSources(sourceSet, context) } fun transformDocumentationModelBeforeMerge( modulesFromPlatforms: List, context: DokkaContext ) = context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc) } fun mergeDocumentationModels( modulesFromPlatforms: List, context: DokkaContext ) = context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms, context) fun transformDocumentationModelAfterMerge( documentationModel: DModule, context: DokkaContext ) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } fun createPages( transformedDocumentation: DModule, context: DokkaContext ) = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) fun createAllModulePage( context: DokkaContext ) = context.single(CoreExtensions.allModulePageCreator).invoke() fun transformPages( pages: RootPageNode, context: DokkaContext ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } fun transformAllModulesPage( pages: RootPageNode, context: DokkaContext ) = context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) } fun render( transformedPages: RootPageNode, context: DokkaContext ) { val renderer = context.single(CoreExtensions.renderer) renderer.render(transformedPages) } fun reportAfterRendering(context: DokkaContext) { context.unusedPoints.takeIf { it.isNotEmpty() }?.also { logger.warn("Unused extension points found: ${it.joinToString(", ")}") } logger.report() if (context.configuration.failOnWarning && (logger.warningsCount > 0 || logger.errorsCount > 0)) { throw DokkaException( "Failed with warningCount=${logger.warningsCount} and errorCount=${logger.errorsCount}" ) } } private fun translateSources(sourceSet: DokkaSourceSet, context: DokkaContext) = context[CoreExtensions.sourceToDocumentableTranslator].map { it.invoke(sourceSet, context) } } private class Timer(startTime: Long, private val logger: DokkaLogger?) { private val steps = mutableListOf("" to startTime) fun report(name: String) { logger?.progress(name) steps += (name to System.currentTimeMillis()) } fun dump(prefix: String = "") { println(prefix) val namePad = steps.map { it.first.length }.max() ?: 0 val timePad = steps.windowed(2).map { (p1, p2) -> p2.second - p1.second }.max()?.toString()?.length ?: 0 steps.windowed(2).forEach { (p1, p2) -> if (p1.first.isNotBlank()) { println("${p1.first.padStart(namePad)}: ${(p2.second - p1.second).toString().padStart(timePad)}") } } } } private fun timed(logger: DokkaLogger? = null, block: Timer.() -> Unit): Timer = Timer(System.currentTimeMillis(), logger).apply { try { block() } finally { report("") } }