diff options
Diffstat (limited to 'core/src/main')
18 files changed, 444 insertions, 180 deletions
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt index 3f7f616f..15d0a26b 100644 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM @@ -133,7 +134,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { info, content -> JvmPackagePartProvider(environment, content.moduleContentScope) }, - builtIns = builtIns + builtIns = { builtIns }, + modulePlatforms = { MultiTargetPlatform.Specific("JVM") } ) resolverForProject.resolverForModule(library) // Required before module to initialize library properly diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index eb2b2a65..8038089f 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -10,6 +10,11 @@ fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition { urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it }) } +fun parseSourceRoot(sourceRoot: String): SourceRoot { + val components = sourceRoot.split("::", limit = 2) + return SourceRoot(components.last(), if (components.size == 1) listOf() else components[0].split(',')) +} + class DokkaBootstrapImpl : DokkaBootstrap { class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { @@ -37,6 +42,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { outputDir: String, format: String, includeNonPublic: Boolean, + includeRootPackage: Boolean, reportUndocumented: Boolean, skipEmptyPackages: Boolean, skipDeprecated: Boolean, @@ -46,7 +52,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { generator = DokkaGenerator( DokkaProxyLogger(logger), classpath, - sources, + sources.map(::parseSourceRoot), samples, includes, moduleName, @@ -54,6 +60,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { outputDir, format, includeNonPublic, + includeRootPackage, reportUndocumented, skipEmptyPackages, skipDeprecated, diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt index cfb7fc03..cb31a1d3 100644 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ b/core/src/main/kotlin/Formats/GFMFormatService.kt @@ -3,11 +3,12 @@ package org.jetbrains.dokka import com.google.inject.Inject open class GFMOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension) + location: Location, + locationService: LocationService, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>) + : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendTable(vararg columns: String, body: () -> Unit) { to.appendln(columns.joinToString(" | ", "| ", " |")) @@ -45,12 +46,14 @@ open class GFMOutputBuilder(to: StringBuilder, open class GFMFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension) { + linkExtension: String, + impliedPlatforms: List<String>) +: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) : this(locationService, signatureGenerator, "md") + signatureGenerator: LanguageService, + impliedPlatforms: List<String>) : this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - GFMOutputBuilder(to, location, locationService, languageService, extension) + GFMOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt index d73e4e62..6819e652 100644 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt @@ -2,6 +2,7 @@ package org.jetbrains.dokka import com.google.inject.Inject import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -11,8 +12,9 @@ open class HtmlOutputBuilder(to: StringBuilder, locationService: LocationService, languageService: LanguageService, extension: String, + impliedPlatforms: List<String>, val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, locationService, languageService, extension) + : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendText(text: String) { to.append(text.htmlEscape()) @@ -95,7 +97,8 @@ open class HtmlOutputBuilder(to: StringBuilder, open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService, signatureGenerator: LanguageService, - val templateService: HtmlTemplateService) + val templateService: HtmlTemplateService, + @Named(impliedPlatformsName) val impliedPlatforms: List<String>) : StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService { override fun enumerateSupportFiles(callback: (String, String) -> Unit) { @@ -103,7 +106,7 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi } override fun createOutputBuilder(to: StringBuilder, location: Location) = - HtmlOutputBuilder(to, location, locationService, languageService, extension, templateService) + HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, templateService) override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { templateService.appendHeader(to, "Module Contents", locationService.calcPathToRoot(location)) diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt index bab73379..d217bf38 100644 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt @@ -1,13 +1,16 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName open class JekyllOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendNodes(nodes: Iterable<DocumentationNode>) { to.appendln("---") @@ -25,14 +28,15 @@ open class JekyllOutputBuilder(to: StringBuilder, open class JekyllFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension) { + linkExtension: String, + impliedPlatforms: List<String>) +: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md") { - } + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - JekyllOutputBuilder(to, location, locationService, languageService, extension) + JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt index 9a43f9c4..7ebc5f09 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -1,14 +1,17 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName open class KotlinWebsiteOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : JekyllOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { private var needHardLineBreaks = false private var insideDiv = 0 @@ -145,11 +148,12 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, } class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) - : JekyllFormatService(locationService, signatureGenerator, "html") + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>) + : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) { override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension) + KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } @@ -157,8 +161,9 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension) { + extension: String, + impliedPlatforms: List<String>) + : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { div(to, "sample", true) { @@ -173,9 +178,10 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, } class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) - : JekyllFormatService(locationService, signatureGenerator, "html") { + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>) + : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) { override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension) + KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt index fc63b2f2..b9c9c04f 100644 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt @@ -1,6 +1,8 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName import java.util.* enum class ListKind { @@ -14,8 +16,9 @@ open class MarkdownOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : StructuredOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { private val listKindStack = Stack<ListKind>() protected var inTableCell = false @@ -211,11 +214,13 @@ open class MarkdownOutputBuilder(to: StringBuilder, open class MarkdownFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) + linkExtension: String, + val impliedPlatforms: List<String>) : StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md") + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, locationService, languageService, extension) + MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index 7896bcd8..3cfc96a2 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka import org.jetbrains.dokka.LanguageService.RenderMode +import org.jetbrains.kotlin.utils.ifEmpty import java.util.* data class FormatLink(val text: String, val href: String) @@ -9,7 +10,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val location: Location, val locationService: LocationService, val languageService: LanguageService, - val extension: String) : FormattedOutputBuilder { + val extension: String, + val impliedPlatforms: List<String>) : FormattedOutputBuilder { protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { to.append(prefix) @@ -59,6 +61,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, abstract fun appendText(text: String) + open fun appendSinceKotlin(version: String) { + appendParagraph { + appendText("Available since Kotlin: ") + appendCode { appendText(version) } + } + } + open fun appendSymbol(text: String) { appendText(text) } @@ -284,12 +293,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } item.appendOverrides() item.appendDeprecation() + item.appendPlatforms() } // All items have exactly the same documentation, so we can use any item to render it val item = items.first() item.details(NodeKind.OverloadGroupNote).forEach { appendContent(it.content) } + appendContent(item.content.summary) item.appendDescription() } @@ -334,6 +345,17 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + private fun DocumentationNode.appendPlatforms() { + val platforms = platformsToShow.ifEmpty { return } + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + platforms.joinToString()) + } + } + + val DocumentationNode.platformsToShow: List<String> + get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it } + private fun DocumentationNode.appendDescription() { if (content.description != ContentEmpty) { appendContent(content.description) @@ -384,15 +406,15 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendSection("Exceptions", node.members(NodeKind.Exception)) appendSection("Type Aliases", node.members(NodeKind.TypeAlias)) appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false) - appendSection("Constructors", node.members(NodeKind.Constructor)) - appendSection("Properties", node.members(NodeKind.Property)) + appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) + appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true) + appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true) appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) - appendSection("Functions", node.members(NodeKind.Function)) + appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true) appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty)) + appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction)) + appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) appendSection("Other members", node.members.filter { it.kind !in setOf( @@ -431,7 +453,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } - private fun appendSection(caption: String, members: List<DocumentationNode>, sortMembers: Boolean = true) { + private fun appendSection(caption: String, members: List<DocumentationNode>, + sortMembers: Boolean = true, + omitSamePlatforms: Boolean = false) { if (members.isEmpty()) return appendHeader(3) { appendText(caption) } @@ -440,11 +464,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val membersMap = children.groupBy { link(node, it) } appendTable("Name", "Summary") { - appendTableBody() { + appendTableBody { for ((memberLocation, members) in membersMap) { - appendTableRow() { + appendTableRow { appendTableCell { appendLink(memberLocation) + appendPlatforms(members, omitSamePlatforms) } appendTableCell { val breakdownBySummary = members.groupBy { it.summary } @@ -478,6 +503,16 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, renderedSignatures.last().appendSignature() } } + + private fun appendPlatforms(items: List<DocumentationNode>, omitSamePlatforms: Boolean) { + val platforms = items.foldRight(items.first().platformsToShow.toSet()) { + node, platforms -> platforms.intersect(node.platformsToShow) + } + if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { + appendLine() + to.append("(${platforms.joinToString()})") + } + } } inner class AllTypesNodeBuilder(val node: DocumentationNode) diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt index 360965d5..6b5df7c9 100644 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt @@ -7,7 +7,8 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiManager -import org.jetbrains.dokka.Utilities.DokkaModule +import org.jetbrains.dokka.Utilities.DokkaAnalysisModule +import org.jetbrains.dokka.Utilities.DokkaOutputModule import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -15,47 +16,76 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.TopDownAnalysisMode import org.jetbrains.kotlin.utils.PathUtil import java.io.File import kotlin.system.measureTimeMillis +class SourceRoot(path: String, val defaultPlatforms: List<String> = emptyList()) { + val path: String = File(path).absolutePath +} + class DokkaGenerator(val logger: DokkaLogger, val classpath: List<String>, - val sources: List<String>, + val sources: List<SourceRoot>, val samples: List<String>, val includes: List<String>, val moduleName: String, val options: DocumentationOptions) { + + private val documentationModule = DocumentationModule(moduleName) + fun generate() { - val environment = createAnalysisEnvironment() + val sourcesGroupedByPlatform = sources.groupBy { it.defaultPlatforms.firstOrNull() } + for ((platform, roots) in sourcesGroupedByPlatform) { + appendSourceModule(platform, roots) + } + documentationModule.prepareForGeneration(options) + + val timeBuild = measureTimeMillis { + logger.info("Generating pages... ") + val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger)) + outputInjector.getInstance(Generator::class.java).buildAll(documentationModule) + } + logger.info("done in ${timeBuild / 1000} secs") + } + + private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) { + val sourcePaths = sourceRoots.map { it.path } + val environment = createAnalysisEnvironment(sourcePaths) logger.info("Module: $moduleName") logger.info("Output: ${File(options.outputDir)}") - logger.info("Sources: ${environment.sources.joinToString()}") + logger.info("Sources: ${sourcePaths.joinToString()}") logger.info("Classpath: ${environment.classpath.joinToString()}") logger.info("Analysing sources and libraries... ") val startAnalyse = System.currentTimeMillis() - val injector = Guice.createInjector(DokkaModule(environment, options, logger)) + val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty() + val defaultPlatformsProvider = object : DefaultPlatformsProvider { + override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> { + val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath + ?.let { File(it).absolutePath } + val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } + return sourceRoot?.defaultPlatforms ?: defaultPlatformAsList + } + } + + val injector = Guice.createInjector( + DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger)) - val documentation = buildDocumentationModule(injector, moduleName, { isSample(it) }, includes) + buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes) val timeAnalyse = System.currentTimeMillis() - startAnalyse logger.info("done in ${timeAnalyse / 1000} secs") - val timeBuild = measureTimeMillis { - logger.info("Generating pages... ") - injector.getInstance(Generator::class.java).buildAll(documentation) - } - logger.info("done in ${timeBuild / 1000} secs") - Disposer.dispose(environment) } - fun createAnalysisEnvironment(): AnalysisEnvironment { + fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment { val environment = AnalysisEnvironment(DokkaMessageCollector(logger)) environment.apply { @@ -65,14 +95,14 @@ class DokkaGenerator(val logger: DokkaLogger, addClasspath(File(element)) } - addSources(this@DokkaGenerator.sources) + addSources(sourcePaths) addSources(this@DokkaGenerator.samples) } return environment } - fun isSample(file: PsiFile): Boolean { + fun isNotSample(file: PsiFile): Boolean { val sourceFile = File(file.virtualFile!!.path) return samples.none { sample -> val canonicalSample = File(sample).canonicalPath @@ -100,9 +130,9 @@ class DokkaMessageCollector(val logger: DokkaLogger): MessageCollector { } fun buildDocumentationModule(injector: Injector, - moduleName: String, + documentationModule: DocumentationModule, filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List<String> = listOf()): DocumentationModule { + includes: List<String> = listOf()) { val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) @@ -120,7 +150,13 @@ fun buildDocumentationModule(injector: Injector, for (include in includes) { packageDocs.parse(include, fragments.firstOrNull()) } - val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent) + if (documentationModule.content.isEmpty()) { + documentationModule.updateContent { + for (node in packageDocs.moduleContent.children) { + append(node) + } + } + } with(injector.getInstance(DocumentationBuilder::class.java)) { documentationModule.appendFragments(fragments, packageDocs.packageContent, @@ -131,10 +167,6 @@ fun buildDocumentationModule(injector: Injector, with(injector.getInstance(JavaDocumentationBuilder::class.java)) { javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } } - - injector.getInstance(NodeReferenceGraph::class.java).resolveReferences() - - return documentationModule } diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 806e9f92..b2f4aeaf 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -44,10 +44,10 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph) { + @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph) + this.docParser = JavadocParser(refGraph, logger) } constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 0fb98230..af45f150 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -20,7 +20,8 @@ interface JavaDocumentationParser { fun parseDocumentation(element: PsiNamedElement): JavadocParseResult } -class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { +class JavadocParser(private val refGraph: NodeReferenceGraph, + private val logger: DokkaLogger) : JavaDocumentationParser { override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment if (docComment == null) return JavadocParseResult.Empty @@ -99,7 +100,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio private fun createLink(element: Element): ContentBlock { val docref = element.attr("docref") if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) + return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)}) } val href = element.attr("href") if (href != null) { @@ -118,7 +119,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio val linkSignature = resolveLink(linkElement) val text = ContentText(linkElement.text) if (linkSignature != null) { - val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookup(linkSignature)}) + val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)}) linkNode.append(text) seeSection.append(linkNode) } else { diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt index 4c46f7d6..71b636bf 100644 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt @@ -31,7 +31,7 @@ class DeclarationLinkResolver if (jdkHref != null) { return ContentExternalLink(jdkHref) } - return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) }) + return ContentNodeLazyLink(href, { -> refGraph.lookupOrWarn(symbol.signature(), logger) }) } if ("/" in href) { return ContentExternalLink(href) diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt index d1f64eeb..06acf2e6 100644 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt @@ -116,7 +116,7 @@ class DescriptorDocumentationParser fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> { val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser(refGraph).parseDocumentation(psi as PsiNamedElement) + val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement) return parseResult.content to { node -> parseResult.deprecatedContent?.let { val deprecationNode = DocumentationNode("", it, NodeKind.Modifier) diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt index afb95fe6..2ab43fa1 100644 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -28,16 +28,19 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.supertypes +import com.google.inject.name.Named as GuiceNamed data class DocumentationOptions(val outputDir: String, val outputFormat: String, val includeNonPublic: Boolean = false, + val includeRootPackage: Boolean = false, val reportUndocumented: Boolean = true, val skipEmptyPackages: Boolean = true, val skipDeprecated: Boolean = false, val jdkVersion: Int = 6, val generateIndexPages: Boolean = true, - val sourceLinks: List<SourceLinkDefinition>) + val sourceLinks: List<SourceLinkDefinition> = emptyList(), + val impliedPlatforms: List<String> = emptyList()) private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor, extensionReceiverDescriptor: DeclarationDescriptor, @@ -57,13 +60,19 @@ interface PackageDocumentationBuilder { allFqNames: Collection<FqName>) } +interface DefaultPlatformsProvider { + fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> +} + class DocumentationBuilder @Inject constructor(val resolutionFacade: DokkaResolutionFacade, val descriptorDocumentationParser: DescriptorDocumentationParser, val options: DocumentationOptions, val refGraph: NodeReferenceGraph, + val platformNodeRegistry: PlatformNodeRegistry, val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver) { + val linkResolver: DeclarationLinkResolver, + val defaultPlatformsProvider: DefaultPlatformsProvider) { val boringBuiltinClasses = setOf( "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") @@ -156,6 +165,11 @@ class DocumentationBuilder return appendType(it.abbreviation) } + if (kotlinType.isDynamic()) { + append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail) + return + } + val classifierDescriptor = kotlinType.constructor.declarationDescriptor val name = when (classifierDescriptor) { is ClassDescriptor -> { @@ -199,17 +213,34 @@ class DocumentationBuilder fun DocumentationNode.appendAnnotations(annotated: Annotated) { annotated.annotations.forEach { it.build()?.let { annotationNode -> - val refKind = when { - it.isDocumented() && annotationNode.isDeprecation() -> RefKind.Deprecation - it.isDocumented() -> RefKind.Annotation - it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation - else -> return@forEach + if (annotationNode.isSinceKotlin()) { + appendSinceKotlin(annotationNode) } - append(annotationNode, refKind) + else { + val refKind = when { + it.isDocumented() -> + when { + annotationNode.isDeprecation() -> RefKind.Deprecation + else -> RefKind.Annotation + } + it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation + else -> return@forEach + } + append(annotationNode, refKind) + } + } } } + fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) { + var kotlinVersion = annotation.detail(NodeKind.Parameter).detail(NodeKind.Value).name + if (kotlinVersion.startsWith('\"') && kotlinVersion.endsWith('\"')) { + kotlinVersion = kotlinVersion.substring(1..kotlinVersion.length-2) + } + append(platformNodeRegistry["Kotlin " + kotlinVersion], RefKind.Platform) + } + fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach { @@ -219,8 +250,16 @@ class DocumentationBuilder } } + fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" + fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation + fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { appendSourceLink(sourceElement.getPsi(), options.sourceLinks) } @@ -230,11 +269,7 @@ class DocumentationBuilder } fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? { - // do not include generated code - if (descriptor is CallableMemberDescriptor && descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) - return null - - if (descriptor.isDocumented(options)) { + if (!descriptor.isGenerated() && descriptor.isDocumented(options)) { val node = descriptor.build() append(node, kind) return node @@ -242,21 +277,57 @@ class DocumentationBuilder return null } - fun DocumentationNode.appendMembers(descriptors: Iterable<DeclarationDescriptor>, - inheritedLinkKind: RefKind = RefKind.InheritedMember): List<DocumentationNode> { - val nodes = descriptors.map { descriptor -> - if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() - if (baseDescriptor != null) { - link(this, baseDescriptor, inheritedLinkKind) + fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { + if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return + + val existingNode = refGraph.lookup(descriptor.signature()) + if (existingNode != null) { + existingNode.updatePlatforms(descriptor) + + if (descriptor is ClassDescriptor) { + val membersToDocument = descriptor.collectMembersToDocument() + for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) { + if (memberDescriptor is ClassDescriptor) { + existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes + } + else { + val existingMemberNode = refGraph.lookup(memberDescriptor.signature()) + if (existingMemberNode != null) { + existingMemberNode.updatePlatforms(memberDescriptor) + } + else { + existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier) + } + } } - null - } else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - appendChild(descriptorToUse, RefKind.Member) } } - return nodes.filterNotNull() + else { + appendChild(descriptor, RefKind.Member) + } + } + + private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + + fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor, + inheritedLinkKind: RefKind = RefKind.InheritedMember, + extraModifier: String?) { + if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() + if (baseDescriptor != null) { + link(this, baseDescriptor, inheritedLinkKind) + } + } else { + val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original + val child = appendChild(descriptorToUse, RefKind.Member) + if (extraModifier != null) { + child?.appendTextNode("static", NodeKind.Modifier) + } + } } fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { @@ -272,6 +343,7 @@ class DocumentationBuilder val allFqNames = fragments.map { it.fqName }.distinct() for (packageName in allFqNames) { + if (packageName.isRoot && !options.includeRootPackage) continue val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue @@ -282,9 +354,6 @@ class DocumentationBuilder } propagateExtensionFunctionsToSubclasses(fragments) - if (options.generateIndexPages) { - generateAllTypesNode() - } } private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) { @@ -297,13 +366,15 @@ class DocumentationBuilder .filter { it.extensionReceiverParameter != null } val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name } - allExtensionFunctions.forEach { extensionFunction -> + for (extensionFunction in allExtensionFunctions) { + if (extensionFunction.dispatchReceiverParameter != null) continue val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name] ?.filter { fn -> fn.canShadow(extensionFunction) } ?: emptyList() - val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: return@forEach - val subclasses = classHierarchy[classDescriptor] ?: return@forEach + if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue + val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue + val subclasses = classHierarchy[classDescriptor] ?: continue subclasses.forEach { subclass -> if (subclass.isExtensionApplicable(extensionFunction) && possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) { @@ -357,19 +428,6 @@ class DocumentationBuilder return false } - private fun DocumentationNode.generateAllTypesNode() { - val allTypes = members(NodeKind.Package) - .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } } - .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name } - - val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) - for (typeNode in allTypes) { - allTypesNode.addReferenceTo(typeNode, RefKind.Member) - } - - append(allTypesNode, RefKind.Member) - } - fun DeclarationDescriptor.build(): DocumentationNode = when (this) { is ClassDescriptor -> build() is ConstructorDescriptor -> build() @@ -392,6 +450,7 @@ class DocumentationBuilder node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) node.appendSourceLink(source) + node.appendDefaultPlatforms(this) register(this, node) return node @@ -413,28 +472,48 @@ class DocumentationBuilder } if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS) + } + for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) { + node.appendClassMember(descriptor, inheritedLinkKind, extraModifier) + } + node.appendAnnotations(this) + node.appendModifiers(this) + node.appendSourceLink(source) + node.appendDefaultPlatforms(this) + register(this, node) + return node + } + + data class ClassMember(val descriptor: DeclarationDescriptor, + val inheritedLinkKind: RefKind = RefKind.InheritedMember, + val extraModifier: String? = null) + + fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> { + val result = arrayListOf<ClassMember>() + if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) { + val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS) constructors.filter { it.valueParameters.size > 0 } else constructors - node.appendMembers(constructorsToDocument) - } - val members = defaultType.memberScope.getContributedDescriptors().filter { it != companionObjectDescriptor } - node.appendMembers(members) - node.appendMembers(staticScope.getContributedDescriptors()).forEach { - it.appendTextNode("static", NodeKind.Modifier) + constructorsToDocument.mapTo(result) { ClassMember(it) } } + + defaultType.memberScope.getContributedDescriptors() + .filter { it != companionObjectDescriptor } + .mapTo(result) { ClassMember(it) } + + staticScope.getContributedDescriptors() + .mapTo(result) { ClassMember(it, extraModifier = "static") } + val companionObjectDescriptor = companionObjectDescriptor if (companionObjectDescriptor != null) { val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors() val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() } - node.appendMembers(descriptorsToDocument, RefKind.InheritedCompanionObjectMember) + descriptorsToDocument.mapTo(result) { + ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember) + } } - node.appendAnnotations(this) - node.appendModifiers(this) - node.appendSourceLink(source) - register(this, node) - return node + return result } fun CallableDescriptor.isInheritedFromAny(): Boolean { @@ -449,6 +528,7 @@ class DocumentationBuilder fun ConstructorDescriptor.build(): DocumentationNode { val node = nodeForDescriptor(this, NodeKind.Constructor) node.appendInPageChildren(valueParameters, RefKind.Detail) + node.appendDefaultPlatforms(this) register(this, node) return node } @@ -477,6 +557,7 @@ class DocumentationBuilder node.appendModifiers(this) node.appendSourceLink(source) node.appendSignature(this) + node.appendDefaultPlatforms(this) overriddenDescriptors.forEach { addOverrideLink(it, this) @@ -523,6 +604,7 @@ class DocumentationBuilder overriddenDescriptors.forEach { addOverrideLink(it, this) } + node.appendDefaultPlatforms(this) register(this, node) return node @@ -596,9 +678,10 @@ class DocumentationBuilder receiverClass = upperBoundClass } } - link(receiverClass, - containingDeclaration, - RefKind.Extension) + + if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) { + link(receiverClass, containingDeclaration, RefKind.Extension) + } val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver) node.appendType(type) @@ -646,6 +729,8 @@ fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean { (!options.skipDeprecated || !isDeprecated()) } +private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION + class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, packageName: FqName, @@ -657,7 +742,7 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { with(documentationBuilder) { if (descriptor.isDocumented(options)) { val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames) - parent.appendChild(descriptor, RefKind.Member) + parent.appendOrUpdateMember(descriptor) } } } @@ -819,3 +904,23 @@ fun DeclarationDescriptor.sourceLocation(): String? { } return null } + +fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) { + if (options.generateIndexPages) { + generateAllTypesNode() + } + nodeRefGraph.resolveReferences() +} + +fun DocumentationNode.generateAllTypesNode() { + val allTypes = members(NodeKind.Package) + .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } } + .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name } + + val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) + for (typeNode in allTypes) { + allTypesNode.addReferenceTo(typeNode, RefKind.Member) + } + + append(allTypesNode, RefKind.Member) +} diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt index 75a8a948..e54772b3 100644 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt @@ -151,26 +151,47 @@ class KotlinLanguageService : LanguageService { } } - private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) { var typeArguments = node.details(NodeKind.Type) - if (node.name == "Function${typeArguments.count() - 1}") { - // lambda - val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } - if (isExtension) { - renderType(typeArguments.first(), renderMode) - symbol(".") - typeArguments = typeArguments.drop(1) - } - symbol("(") - renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { - renderFunctionalTypeParameterName(it, renderMode) - renderType(it, renderMode) - } - symbol(")") - nbsp() - symbol("->") - nbsp() - renderType(typeArguments.last(), renderMode) + + if (node.name.startsWith("Suspend")) { + keyword("suspend ") + } + + // lambda + val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } + if (isExtension) { + renderType(typeArguments.first(), renderMode) + symbol(".") + typeArguments = typeArguments.drop(1) + } + symbol("(") + renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { + renderFunctionalTypeParameterName(it, renderMode) + renderType(it, renderMode) + } + symbol(")") + nbsp() + symbol("->") + nbsp() + renderType(typeArguments.last(), renderMode) + + } + + private fun DocumentationNode.isFunctionalType(): Boolean { + val typeArguments = details(NodeKind.Type) + val functionalTypeName = "Function${typeArguments.count() - 1}" + val suspendFunctionalTypeName = "Suspend$functionalTypeName" + return name == functionalTypeName || name == suspendFunctionalTypeName + } + + private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + if (node.name == "dynamic") { + keyword("dynamic") + return + } + if (node.isFunctionalType()) { + renderFunctionalType(node, renderMode) return } if (renderMode == RenderMode.FULL) { @@ -178,7 +199,8 @@ class KotlinLanguageService : LanguageService { } renderModifiersForNode(node, renderMode, true) renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) } - if (typeArguments.any()) { + val typeArguments = node.details(NodeKind.Type) + if (typeArguments.isNotEmpty()) { symbol("<") renderList(typeArguments, noWrap = true) { renderType(it, renderMode) diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index def0f626..caacac14 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -48,6 +48,7 @@ enum class NodeKind { Signature, ExternalLink, + Platform, AllTypes, @@ -97,6 +98,8 @@ open class DocumentationNode(val name: String, get() = references(RefKind.Annotation).map { it.to } val deprecation: DocumentationNode? get() = references(RefKind.Deprecation).singleOrNull()?.to + val platforms: List<String> + get() = references(RefKind.Platform).map { it.to.name } // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice fun addReferenceTo(to: DocumentationNode, kind: RefKind) { @@ -135,6 +138,7 @@ open class DocumentationNode(val name: String, class DocumentationModule(name: String, content: Content = Content.Empty) : DocumentationNode(name, content, NodeKind.Module) { + val nodeRefGraph = NodeReferenceGraph() } val DocumentationNode.path: List<DocumentationNode> diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt index 0165b567..a968f400 100644 --- a/core/src/main/kotlin/Model/DocumentationReference.kt +++ b/core/src/main/kotlin/Model/DocumentationReference.kt @@ -1,6 +1,5 @@ package org.jetbrains.dokka -import com.google.inject.Inject import com.google.inject.Singleton enum class RefKind { @@ -18,7 +17,8 @@ enum class RefKind { Annotation, HiddenAnnotation, Deprecation, - TopLevelPage + TopLevelPage, + Platform } data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) { @@ -36,9 +36,7 @@ class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?, } } -@Singleton -class NodeReferenceGraph - @Inject constructor(val logger: DokkaLogger) { +class NodeReferenceGraph() { private val nodeMap = hashMapOf<String, DocumentationNode>() val references = arrayListOf<PendingDocumentationReference>() @@ -58,7 +56,9 @@ class NodeReferenceGraph references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind)) } - fun lookup(signature: String): DocumentationNode? { + fun lookup(signature: String) = nodeMap[signature] + + fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? { val result = nodeMap[signature] if (result == null) { logger.warn("Can't find node by signature $signature") @@ -70,3 +70,14 @@ class NodeReferenceGraph references.forEach { it.resolve() } } } + +@Singleton +class PlatformNodeRegistry { + private val platformNodes = hashMapOf<String, DocumentationNode>() + + operator fun get(platform: String): DocumentationNode { + return platformNodes.getOrPut(platform) { + DocumentationNode(platform, Content.Empty, NodeKind.Platform) + } + } +} diff --git a/core/src/main/kotlin/Utilities/DokkaModule.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt index e1ae829a..5982d7dd 100644 --- a/core/src/main/kotlin/Utilities/DokkaModule.kt +++ b/core/src/main/kotlin/Utilities/DokkaModules.kt @@ -3,6 +3,7 @@ package org.jetbrains.dokka.Utilities import com.google.inject.Binder import com.google.inject.Module import com.google.inject.Provider +import com.google.inject.TypeLiteral import com.google.inject.name.Names import org.jetbrains.dokka.* import org.jetbrains.dokka.Formats.FormatDescriptor @@ -10,10 +11,47 @@ import org.jetbrains.dokka.Samples.SampleProcessingService import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import java.io.File -class DokkaModule(val environment: AnalysisEnvironment, - val options: DocumentationOptions, - val logger: DokkaLogger) : Module { +const val impliedPlatformsName = "impliedPlatforms" + +class DokkaAnalysisModule(val environment: AnalysisEnvironment, + val options: DocumentationOptions, + val defaultPlatformsProvider: DefaultPlatformsProvider, + val nodeReferenceGraph: NodeReferenceGraph, + val logger: DokkaLogger) : Module { + override fun configure(binder: Binder) { + val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat) + + binder.registerCategory<LanguageService>("language") + binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java) + binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java) + binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java) + + val coreEnvironment = environment.createCoreEnvironment() + binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) + + val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) + binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) + + binder.bind<DocumentationOptions>().toInstance(options) + binder.bind<DokkaLogger>().toInstance(logger) + + binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider) + + binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph) + } +} + +object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>() + +class DokkaOutputModule(val options: DocumentationOptions, + val logger: DokkaLogger) : Module { override fun configure(binder: Binder) { + binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java) + + binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> { + override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css") + }) + binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir)) binder.bindNameAnnotated<LocationService, SingleFolderLocationService>("singleFolder") @@ -24,13 +62,7 @@ class DokkaModule(val environment: AnalysisEnvironment, // defaults binder.bind(LocationService::class.java).to(FoldersLocationService::class.java) binder.bind(FileLocationService::class.java).to(FoldersLocationService::class.java) - binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java) - - binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> { - override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css") - }) - binder.registerCategory<LanguageService>("language") binder.registerCategory<OutlineFormatService>("outline") binder.registerCategory<FormatService>("format") binder.registerCategory<Generator>("generator") @@ -43,20 +75,12 @@ class DokkaModule(val environment: AnalysisEnvironment, descriptor.formatServiceClass?.let { clazz -> binder.bind(FormatService::class.java).to(clazz.java) } - binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java) - binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java) - binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java) binder.bind<Generator>().to(descriptor.generatorServiceClass.java) - val coreEnvironment = environment.createCoreEnvironment() - binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) - - val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) - binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) - binder.bind<DocumentationOptions>().toInstance(options) binder.bind<DokkaLogger>().toInstance(logger) + binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms) } } |