From 50111daf07c7afd1c1b60e9672ed6786c96efdea Mon Sep 17 00:00:00 2001 From: Paweł Marks Date: Thu, 31 Oct 2019 15:26:20 +0100 Subject: Make things compile, no matter the cost --- core/build.gradle | 4 +- core/src/main/kotlin/DokkaBootstrapImpl.kt | 76 --- core/src/main/kotlin/Formats/AnalysisComponents.kt | 45 -- core/src/main/kotlin/Formats/FormatDescriptor.kt | 43 -- core/src/main/kotlin/Generation/DokkaGenerator.kt | 214 --------- .../kotlin/Java/JavaPsiDocumentationBuilder.kt | 344 ------------- core/src/main/kotlin/Java/JavadocParser.kt | 402 ---------------- .../kotlin/Kotlin/DescriptorDocumentationParser.kt | 188 -------- .../src/main/kotlin/Kotlin/DocumentationBuilder.kt | 534 --------------------- .../Kotlin/KotlinElementSignatureProvider.kt | 33 -- .../main/kotlin/Kotlin/KotlinLanguageService.kt | 473 ------------------ .../main/kotlin/Languages/CommonLanguageService.kt | 84 ---- core/src/main/kotlin/Languages/LanguageService.kt | 41 -- core/src/main/kotlin/Model/PackageDocs.kt | 136 ------ core/src/main/kotlin/Utilities/DokkaModules.kt | 82 ---- .../transformers/DocumentationToPageTransformer.kt | 2 +- 16 files changed, 3 insertions(+), 2698 deletions(-) delete mode 100644 core/src/main/kotlin/DokkaBootstrapImpl.kt delete mode 100644 core/src/main/kotlin/Formats/AnalysisComponents.kt delete mode 100644 core/src/main/kotlin/Formats/FormatDescriptor.kt delete mode 100644 core/src/main/kotlin/Generation/DokkaGenerator.kt delete mode 100644 core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt delete mode 100644 core/src/main/kotlin/Java/JavadocParser.kt delete mode 100644 core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt delete mode 100644 core/src/main/kotlin/Kotlin/DocumentationBuilder.kt delete mode 100644 core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt delete mode 100644 core/src/main/kotlin/Kotlin/KotlinLanguageService.kt delete mode 100644 core/src/main/kotlin/Languages/CommonLanguageService.kt delete mode 100644 core/src/main/kotlin/Languages/LanguageService.kt delete mode 100644 core/src/main/kotlin/Model/PackageDocs.kt delete mode 100644 core/src/main/kotlin/Utilities/DokkaModules.kt (limited to 'core') diff --git a/core/build.gradle b/core/build.gradle index f3fe35e4..ea1ab325 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -21,7 +21,7 @@ allprojects { } dependencies { - compile project(":integration") +// compile project(":integration") compile project(path: ":coreDependencies", configuration: "shadow") compile "org.jetbrains.kotlin:kotlin-stdlib:$bundled_kotlin_compiler_version" @@ -50,7 +50,7 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-stdlib-js:$bundled_kotlin_compiler_version" testImplementation "org.jetbrains.kotlin:kotlin-stdlib-common:$bundled_kotlin_compiler_version" - testImplementation project(":core:testApi") +// testImplementation project(":core:testApi") testCompile ideaRT() } diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt deleted file mode 100644 index b48b62d4..00000000 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.jetbrains.dokka - -import com.google.gson.Gson -import org.jetbrains.dokka.DokkaConfiguration.PackageOptions - -import java.util.function.BiConsumer - - -fun parsePerPackageOptions(arg: String): List { - if (arg.isBlank()) return emptyList() - - return arg.split(";").map { it.split(",") }.map { - val prefix = it.first() - if (prefix == "") - throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead") - val args = it.subList(1, it.size) - val deprecated = args.find { it.endsWith("deprecated") }?.startsWith("+") ?: true - val reportUndocumented = args.find { it.endsWith("warnUndocumented") }?.startsWith("+") ?: true - val privateApi = args.find { it.endsWith("privateApi") }?.startsWith("+") ?: false - val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") ?: false - PackageOptionsImpl(prefix, includeNonPublic = privateApi, reportUndocumented = reportUndocumented, skipDeprecated = !deprecated, suppress = suppress) - } -} - -class DokkaBootstrapImpl : DokkaBootstrap { - - private class DokkaProxyLogger(val consumer: BiConsumer) : DokkaLogger { - override fun info(message: String) { - consumer.accept("info", message) - } - - override fun warn(message: String) { - consumer.accept("warn", message) - } - - override fun error(message: String) { - consumer.accept("error", message) - } - } - - lateinit var generator: DokkaGenerator - val gson = Gson() - - fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) = with(configuration) { - - fun defaultLinks(config: PassConfigurationImpl): List { - val links = mutableListOf() - if (!config.noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/") - .build() as ExternalDocumentationLinkImpl - - if (!config.noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") - .build() as ExternalDocumentationLinkImpl - return links - } - - val configurationWithLinks = - configuration.copy(passesConfigurations = - passesConfigurations - .map { - val links: List = it.externalDocumentationLinks + defaultLinks(it) - it.copy(externalDocumentationLinks = links) - } - ) - - generator = DokkaGenerator(configurationWithLinks, logger) - } - - override fun configure(logger: BiConsumer, serializedConfigurationJSON: String) - = configure(DokkaProxyLogger(logger), gson.fromJson(serializedConfigurationJSON, DokkaConfigurationImpl::class.java)) - - override fun generate() = generator.generate() -} diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt deleted file mode 100644 index d78d4a0c..00000000 --- a/core/src/main/kotlin/Formats/AnalysisComponents.kt +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider -import org.jetbrains.dokka.KotlinElementSignatureProvider -import org.jetbrains.dokka.ElementSignatureProvider -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface DefaultAnalysisComponentServices { - val packageDocumentationBuilderClass: KClass - val javaDocumentationBuilderClass: KClass - val sampleProcessingService: KClass - val elementSignatureProvider: KClass -} - -interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { - override fun configureAnalysis(binder: Binder): Unit = with(binder) { - bind() toType elementSignatureProvider - bind() toType packageDocumentationBuilderClass - bind() toType javaDocumentationBuilderClass - bind() toType sampleProcessingService - } -} - - -object KotlinAsJava : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class - override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class -} - - -object KotlinAsKotlin : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class -} \ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt deleted file mode 100644 index 4bac8aa0..00000000 --- a/core/src/main/kotlin/Formats/FormatDescriptor.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.lazyBind -import org.jetbrains.dokka.Utilities.toOptional -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface FormatDescriptorAnalysisComponent { - fun configureAnalysis(binder: Binder) -} - -interface FormatDescriptorOutputComponent { - fun configureOutput(binder: Binder) -} - -interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent - - -abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor { - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind() toType NodeLocationAwareGenerator::class - bind() toType generatorServiceClass - bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847 - - bind() toType languageServiceClass - - lazyBind() toOptional (outlineServiceClass) - lazyBind() toOptional formatServiceClass - lazyBind() toOptional packageListServiceClass - } - - abstract val formatServiceClass: KClass? - abstract val outlineServiceClass: KClass? - abstract val generatorServiceClass: KClass - abstract val packageListServiceClass: KClass? - - open val languageServiceClass: KClass = KotlinLanguageService::class -} \ No newline at end of file diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt deleted file mode 100644 index 51d70fbd..00000000 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ /dev/null @@ -1,214 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Guice -import com.google.inject.Injector -import com.intellij.openapi.util.Disposer -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.DokkaAnalysisModule -import org.jetbrains.dokka.Utilities.DokkaRunModule -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -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.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer -import org.jetbrains.kotlin.resolve.TopDownAnalysisMode -import org.jetbrains.kotlin.utils.PathUtil -import java.io.File - -class DokkaGenerator( - val dokkaConfiguration: DokkaConfiguration, - val logger: DokkaLogger -) { - - private val documentationModules: MutableList = mutableListOf() - private val globalInjector = Guice.createInjector(DokkaRunModule(dokkaConfiguration)) - - - fun generate() = with(dokkaConfiguration) { - - - for (pass in passesConfigurations) { - val documentationModule = DocumentationNodes.Module(pass.moduleName) - appendSourceModule(pass, documentationModule) - documentationModules.add(documentationModule) - } - } - - private fun appendSourceModule( - passConfiguration: DokkaConfiguration.PassConfiguration, - documentationModule: DocumentationNodes.Module - ) = with(passConfiguration) { - - val sourcePaths = passConfiguration.sourceRoots.map { it.path } - val environment = createAnalysisEnvironment(sourcePaths, passConfiguration) - - logger.info("Module: $moduleName") - logger.info("Output: ${File(dokkaConfiguration.outputDir)}") - logger.info("Sources: ${sourcePaths.joinToString()}") - logger.info("Classpath: ${environment.classpath.joinToString()}") - - logger.info("Analysing sources and libraries... ") - val startAnalyse = System.currentTimeMillis() - - val defaultPlatformAsList = passConfiguration.targets - val defaultPlatformsProvider = object : DefaultPlatformsProvider { - override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List { -// val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath -// ?.let { File(it).absolutePath } -// val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } - if (descriptor is MemberDescriptor && descriptor.isExpect) { - return defaultPlatformAsList.take(1) - } - return /*sourceRoot?.platforms ?: */defaultPlatformAsList - } - } - - val injector = globalInjector.createChildInjector( - DokkaAnalysisModule(environment, dokkaConfiguration, defaultPlatformsProvider, passConfiguration, logger) - ) - - buildDocumentationModule( - injector, - documentationModule, - { isNotSample(it, passConfiguration.samples) }, - includes - ) - - val timeAnalyse = System.currentTimeMillis() - startAnalyse - logger.info("done in ${timeAnalyse / 1000} secs") - - Disposer.dispose(environment) - } - - fun createAnalysisEnvironment( - sourcePaths: List, - passConfiguration: DokkaConfiguration.PassConfiguration - ): AnalysisEnvironment { - val environment = AnalysisEnvironment(DokkaMessageCollector(logger), passConfiguration.analysisPlatform) - - environment.apply { - if (analysisPlatform == Platform.jvm) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath()) - for (element in passConfiguration.classpath) { - addClasspath(File(element)) - } - - addSources(sourcePaths) - addSources(passConfiguration.samples) - - loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion) - } - - return environment - } - - private fun isNotSample(file: PsiFile, samples: List): Boolean { - val sourceFile = File(file.virtualFile!!.path) - return samples.none { sample -> - val canonicalSample = File(sample).canonicalPath - val canonicalSource = sourceFile.canonicalPath - canonicalSource.startsWith(canonicalSample) - } - } -} - -class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector { - override fun clear() { - seenErrors = false - } - - private var seenErrors = false - - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { - if (severity == CompilerMessageSeverity.ERROR) { - seenErrors = true - } - logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) - } - - override fun hasErrors() = seenErrors -} - -fun buildDocumentationModule( - injector: Injector, - documentationModule: DocumentationNodes.Module, - filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List = listOf() -) { - - val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) - val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) - - val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java) - val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java) - analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) - - val fragments = fragmentFiles.mapNotNull { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) } - .distinct() - - val packageDocs = injector.getInstance(PackageDocs::class.java) - for (include in includes) { - packageDocs.parse(include, fragments) - } - - parseJavaPackageDocs(packageDocs, coreEnvironment) - - with(injector.getInstance(DocumentationBuilder::class.java)) { - documentationModule.appendFragments( - fragments, - injector.getInstance(PackageDocumentationBuilder::class.java) - ) - - propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade) - } - - val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter) - with(injector.getInstance(JavaDocumentationBuilder::class.java)) { - javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } - } -} - -fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) { - val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance() - ?.map { it.file } - ?: listOf() - contentRoots.forEach { root -> - root.walkTopDown().filter { it.name == "overview.html" }.forEach { - packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", ".")) - } - } -} - - -fun KotlinCoreEnvironment.getJavaSourceFiles(): List { - val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance() - ?.map { it.file } - ?: listOf() - - val result = arrayListOf() - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - sourceRoots.forEach { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().forEach { - val vFile = localFileSystem.findFileByPath(it.path) - if (vFile != null) { - val psiFile = PsiManager.getInstance(project).findFile(vFile) - if (psiFile is PsiJavaFile) { - result.add(psiFile) - } - } - } - } - return result -} \ No newline at end of file diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt deleted file mode 100644 index d3fc7048..00000000 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ /dev/null @@ -1,344 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.* -import com.intellij.psi.impl.JavaConstantExpressionEvaluator -import com.intellij.psi.util.InheritanceUtil -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation -import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtModifierListOwner - -fun getSignature(element: PsiElement?) = when(element) { - is PsiPackage -> element.qualifiedName - is PsiClass -> element.qualifiedName - is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name - is PsiMethod -> - methodSignature(element) - is PsiParameter -> { - val method = (element.parent.parent as PsiMethod) - methodSignature(method) - } - else -> null -} - -private fun methodSignature(method: PsiMethod): String { - return method.containingClass!!.qualifiedName + "$" + method.name + "(" + - method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")" -} - -private fun PsiType.typeSignature(): String = when(this) { - is PsiArrayType -> "Array((${componentType.typeSignature()}))" - is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize() - else -> mapTypeName(this) -} - -private fun mapTypeName(psiType: PsiType): String = when (psiType) { - is PsiPrimitiveType -> psiType.canonicalText - is PsiClassType -> psiType.resolve()?.qualifiedName ?: psiType.className - is PsiEllipsisType -> mapTypeName(psiType.componentType) - is PsiArrayType -> "kotlin.Array" - else -> psiType.canonicalText -} - -interface JavaDocumentationBuilder { - fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) -} - -class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { - private val passConfiguration: DokkaConfiguration.PassConfiguration - private val refGraph: NodeReferenceGraph - private val docParser: JavaDocumentationParser - private val documentationBuilder: DocumentationBuilder - - @Inject constructor( - documentationBuilder: DocumentationBuilder, - refGraph: NodeReferenceGraph, - logger: DokkaLogger, - signatureProvider: ElementSignatureProvider, - externalDocumentationLinkResolver: ExternalDocumentationLinkResolver - ) { - this.passConfiguration = documentationBuilder.passConfiguration - this.documentationBuilder = documentationBuilder - this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) - } - - constructor(documentationBuilder: DocumentationBuilder, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { - this.passConfiguration = documentationBuilder.passConfiguration - this.refGraph = refGraph - this.docParser = docParser - this.documentationBuilder = documentationBuilder - } - - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) { - if (skipFile(file) || file.classes.all { skipElement(it) }) { - return - } - val packageNode = documentationBuilder.findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) - appendClasses(packageNode, file.classes) - } - - fun appendClasses(packageNode: DocumentationNode, classes: Array) { - packageNode.appendChildren(classes) { build() } - } - - fun register(element: PsiElement, node: DocumentationNode) { - val signature = getSignature(element) - if (signature != null) { - refGraph.register(signature, node) - } - } - - fun link(node: DocumentationNode, element: PsiElement?) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(node, qualifiedName, RefKind.Link) - } - } - - fun link(element: PsiElement?, node: DocumentationNode, kind: RefKind) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(qualifiedName, node, kind) - } - } - - fun nodeForElement(element: PsiNamedElement, - kind: DocumentationNodes, - name: String = element.name ?: "", - register: Boolean = false): DocumentationNode { - val (docComment, deprecatedContent) = docParser.parseDocumentation(element) - val node = DocumentationNode(name, docComment, kind) - if (register) register(element, node) - if (element is PsiModifierListOwner) { - node.appendModifiers(element) - val modifierList = element.modifierList - if (modifierList != null) { - modifierList.annotations.filter { !ignoreAnnotation(it) }.forEach { - val annotation = it.build() - node.append(annotation, - if (it.qualifiedName == "java.lang.Deprecated") RefKind.Deprecation else RefKind.Annotation) - } - } - } - return node - } - - fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) { - "java.lang.SuppressWarnings" -> true - else -> false - } - - fun DocumentationNode.appendChildren(elements: Array, - kind: RefKind = RefKind.Member, - buildFn: T.() -> DocumentationNode) { - elements.forEach { - if (!skipElement(it)) { - append(it.buildFn(), kind) - } - } - } - - private fun skipFile(javaFile: PsiJavaFile): Boolean = passConfiguration.effectivePackageOptions(javaFile.packageName).suppress - - private fun skipElement(element: Any) = - skipElementByVisibility(element) || - hasSuppressDocTag(element) || - skipElementBySuppressedFiles(element) - - private fun skipElementByVisibility(element: Any): Boolean = - element is PsiModifierListOwner && - element !is PsiParameter && - !(passConfiguration.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && - (element.hasModifierProperty(PsiModifier.PRIVATE) || - element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || - element.isInternal()) - - private fun skipElementBySuppressedFiles(element: Any): Boolean = - element is PsiElement && element.containingFile.virtualFile.path in passConfiguration.suppressedFiles - - private fun PsiElement.isInternal(): Boolean { - val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false - return (ktElement as? KtModifierListOwner)?.hasModifier(KtTokens.INTERNAL_KEYWORD) ?: false - } - - fun DocumentationNode.appendMembers(elements: Array, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Member, buildFn) - - fun DocumentationNode.appendDetails(elements: Array, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Detail, buildFn) - - fun PsiClass.build(): DocumentationNode { - val kind = when { - isAnnotationType -> DocumentationNodes.AnnotationClass - isInterface -> DocumentationNodes.Interface - isEnum -> DocumentationNodes.Enum - isException() -> DocumentationNodes.Exception - else -> DocumentationNodes.Class - } - val node = nodeForElement(this, kind, register = isAnnotationType) - superTypes.filter { !ignoreSupertype(it) }.forEach { - node.appendType(it, DocumentationNodes.Supertype) - val superClass = it.resolve() - if (superClass != null) { - link(superClass, node, RefKind.Inheritor) - } - } - node.appendDetails(typeParameters) { build() } - node.appendMembers(methods) { build() } - node.appendMembers(fields) { build() } - node.appendMembers(innerClasses) { build() } - register(this, node) - return node - } - - fun PsiClass.isException() = InheritanceUtil.isInheritor(this, "java.lang.Throwable") - - fun ignoreSupertype(psiType: PsiClassType): Boolean = - psiType.isClass("java.lang.Enum") || psiType.isClass("java.lang.Object") - - fun PsiClassType.isClass(qName: String): Boolean { - val shortName = qName.substringAfterLast('.') - if (className == shortName) { - val psiClass = resolve() - return psiClass?.qualifiedName == qName - } - return false - } - - fun PsiField.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind()) - node.appendType(type) - - node.appendConstantValueIfAny(this) - register(this, node) - return node - } - - private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) { - val modifierList = field.modifierList ?: return - val initializer = field.initializer ?: return - if (modifierList.hasExplicitModifier(PsiModifier.FINAL) && - modifierList.hasExplicitModifier(PsiModifier.STATIC)) { - val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false) - val text = when(value) { - null -> return // No value found - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - else -> value.toString() - } - append(DocumentationNode(text, Content.Empty, DocumentationNodes.Value), RefKind.Detail) - } - } - - private fun PsiField.nodeKind(): DocumentationNodes = when { - this is PsiEnumConstant -> DocumentationNodes.EnumItem - else -> DocumentationNodes.Field - } - - fun PsiMethod.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind(), - if (isConstructor) "" else name) - - if (!isConstructor) { - node.appendType(returnType) - } - node.appendDetails(parameterList.parameters) { build() } - node.appendDetails(typeParameters) { build() } - register(this, node) - return node - } - - private fun PsiMethod.nodeKind(): DocumentationNodes = when { - isConstructor -> DocumentationNodes.Constructor - else -> DocumentationNodes.Function - } - - fun PsiParameter.build(): DocumentationNode { - val node = nodeForElement(this, DocumentationNodes.Parameter::class) - node.appendType(type) - return node - } - - fun PsiTypeParameter.build(): DocumentationNode { - val node = nodeForElement(this, DocumentationNodes.TypeParameter) - extendsListTypes.forEach { node.appendType(it, DocumentationNodes.UpperBound) } - implementsListTypes.forEach { node.appendType(it, DocumentationNodes.UpperBound) } - return node - } - - fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) { - val modifierList = element.modifierList ?: return - - PsiModifier.MODIFIERS.forEach { - if (modifierList.hasExplicitModifier(it)) { - appendTextNode(it, DocumentationNodes.Modifier) - } - } - } - - fun DocumentationNode.appendType(psiType: PsiType?, kind: DocumentationNodes = DocumentationNodes.Type) { - if (psiType == null) { - return - } - append(psiType.build(kind), RefKind.Detail) - } - - fun PsiType.build(kind: DocumentationNodes = DocumentationNodes.Type): DocumentationNode { - val name = mapTypeName(this) - val node = DocumentationNode(name, Content.Empty, kind) - if (this is PsiClassType) { - node.appendDetails(parameters) { build(DocumentationNodes.Type) } - link(node, resolve()) - } - if (this is PsiArrayType && this !is PsiEllipsisType) { - node.append(componentType.build(DocumentationNodes.Type), RefKind.Detail) - } - return node - } - - private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode { - val existing = refGraph.lookup(getSignature(psiClass)!!) - if (existing != null) return existing - val new = psiClass.build() - val packageNode = documentation. findOrCreatePackageNode(null, (psiClass.containingFile as PsiJavaFile).packageName, emptyMap(), refGraph) - packageNode.append(new, RefKind.Member) - return new - } - - fun PsiAnnotation.build(): DocumentationNode { - - val original = when (this) { - is KtLightAbstractAnnotation -> clsDelegate - else -> this - } - val node = DocumentationNodes.Annotation(qualifiedName?.substringAfterLast(".") ?: "") - val psiClass = original.nameReferenceElement?.resolve() as? PsiClass - if (psiClass != null && psiClass.isAnnotationType) { - node.append(lookupOrBuildClass(psiClass), RefKind.Link) - } - parameterList.attributes.forEach { - val parameter = DocumentationNodes.Parameter(it.name ?: "value", this.extractDescriptor()) - val value = it.value - if (value != null) { - val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text - val valueNode = DocumentationNode(valueText, Content.Empty, DocumentationNodes.Value) - parameter.append(valueNode, RefKind.Detail) - } - node.append(parameter, RefKind.Detail)m - } - return node - } -} - -fun hasSuppressDocTag(element: Any?): Boolean { - val declaration = (element as? KtLightDeclaration<*, *>)?.kotlinOrigin ?: return false - return PsiTreeUtil.findChildrenOfType(declaration.docComment, KDocTag::class.java).any { it.knownTag == KDocKnownTag.SUPPRESS } -} - diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt deleted file mode 100644 index 25a974a3..00000000 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ /dev/null @@ -1,402 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.* -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.* -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.util.containers.isNullOrEmpty -import org.jetbrains.kotlin.utils.keysToMap -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode -import java.net.URI - -data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { - companion object { - val Empty = JavadocParseResult(Content.Empty, null) - } -} - -interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): JavadocParseResult -} - -class JavadocParser( - private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger, - private val signatureProvider: ElementSignatureProvider, - private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) : JavaDocumentationParser { - - private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { - append(LazyContentBlock { - val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList() - listOf(ContentBlock().apply { - append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) - symbol(":") - text(" ") - }) - }) - } - - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return JavadocParseResult.Empty - val result = MutableContent() - var deprecatedContent: Content? = null - - val nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) - val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph } - val firstParagraph = ContentParagraph() - if (firstParagraphContents.isNotEmpty()) { - firstParagraphContents.forEach { firstParagraph.append(it) } - result.append(firstParagraph) - } - - result.appendAll(nodes.drop(firstParagraphContents.size)) - - if (element is PsiMethod) { - val tagsByName = element.searchInheritedTags() - for ((tagName, tags) in tagsByName) { - for ((tag, context) in tags) { - val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) - val signature = signatureProvider.signature(element) - when (tagName) { - "param" -> { - section.appendTypeElement(signature) { - it.details - .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() } - ?.detailOrNull(NodeKind.Type) - } - } - "return" -> { - section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } - } - } - section.appendAll(convertJavadocElements(tag.contentElements(), context)) - } - } - } - - docComment.tags.forEach { tag -> - when (tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content().apply { - appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - in tagsToInherit -> {} - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private val tagsToInherit = setOf("param", "return", "throws") - - private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement) - - private fun PsiMethod.searchInheritedTags(): Map> { - - val output = tagsToInherit.keysToMap { mutableMapOf() } - - fun recursiveSearch(methods: Array) { - for (method in methods) { - recursiveSearch(method.findSuperMethods()) - } - for (method in methods) { - for (tag in method.docComment?.tags.orEmpty()) { - if (tag.name in tagsToInherit) { - output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method) - } - } - } - } - - recursiveSearch(arrayOf(this)) - return output.mapValues { it.value.values } - } - - - private fun PsiDocTag.contentElements(): Iterable { - val tagValueElements = children - .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } - .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } - return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements - } - - private fun convertJavadocElements(elements: Iterable, element: PsiNamedElement): List { - val doc = Jsoup.parse(expandAllForElements(elements, element)) - return doc.body().childNodes().mapNotNull { - convertHtmlNode(it) - } - } - - private fun ContentBlock.appendAll(nodes: List) { - nodes.forEach { append(it) } - } - - private fun expandAllForElements(elements: Iterable, element: PsiNamedElement): String { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it, element)) - } else { - htmlBuilder.append(it.text) - } - } - return htmlBuilder.toString().trim() - } - - private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? { - if (node is TextNode) { - val text = if (insidePre) node.wholeText else node.text() - return ContentText(text) - } else if (node is Element) { - val childBlock = createBlock(node, insidePre) - - node.childNodes().forEach { - val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode) - if (child != null) { - childBlock.append(child) - } - } - return childBlock - } - return null - } - - private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> if (insidePre) ContentBlock() else ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - "br" -> ContentBlock().apply { hardLineBreak() } - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - return when { - element.hasAttr("docref") -> { - val docref = element.attr("docref") - ContentNodeLazyLink(docref) { refGraph.lookupOrWarn(docref, logger)} - } - element.hasAttr("href") -> { - val href = element.attr("href") - - val uri = try { - URI(href) - } catch (_: Exception) { - null - } - - if (uri?.isAbsolute == false) { - ContentLocalLink(href) - } else { - ContentExternalLink(href) - } - } - element.hasAttr("name") -> { - ContentBookmark(element.attr("name")) - } - else -> ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() ?: return - val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val text = ContentText(linkElement.text) - - val linkSignature by lazy { resolveInternalLink(valueElement) } - val node = when { - externalLink != null -> { - val linkNode = ContentExternalLink(externalLink) - linkNode.append(text) - linkNode - } - linkSignature != null -> { - val linkNode = - ContentNodeLazyLink( - (tag.valueElement ?: linkElement).text - ) { refGraph.lookupOrWarn(linkSignature!!, logger) } - linkNode.append(text) - linkNode - } - else -> text - } - seeSection.append(node) - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val linkSignature by lazy { resolveInternalLink(valueElement) } - if (externalLink != null || linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\"" - val link = "${labelText.htmlEscape()}" - if (tag.name == "link") "$link" else link - } else if (valueElement != null) { - valueElement.text - } else { - "" - } - } - "code", "literal" -> { - val text = StringBuilder() - tag.dataElements.forEach { text.append(it.text) } - val escaped = text.toString().trimStart().htmlEscape() - if (tag.name == "code") "$escaped" else escaped - } - "inheritDoc" -> { - val result = (element as? PsiMethod)?.let { - // @{inheritDoc} is only allowed on functions - val parent = tag.parent - when (parent) { - is PsiDocComment -> element.findSuperDocCommentOrWarn() - is PsiDocTag -> element.findSuperDocTagOrWarn(parent) - else -> null - } - } - result ?: tag.text - } - else -> tag.text - } - - private fun PsiDocTag.referenceElement(): PsiElement? = - linkElement()?.let { - if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { - PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) - } else { - it - } - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveExternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return externalDocumentationLinkResolver.buildExternalDocumentationLink(target) - } - return null - } - - private fun resolveInternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return signatureProvider.signature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } - - private fun PsiMethod.findSuperDocCommentOrWarn(): String { - val method = findFirstSuperMethodWithDocumentation(this) - if (method != null) { - val descriptionElements = method.docComment?.descriptionElements?.dropWhile { - it.text.trim().isEmpty() - } ?: return "" - - return expandAllForElements(descriptionElements, method) - } - logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - - private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this) - - if (result != null) { - val (method, tag) = result - - val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() } - - val expandedString = expandAllForElements(contentElements, method) - - return expandedString - } - logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? { - val superMethods = current.findSuperMethods() - for (method in superMethods) { - val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() } - if (!docs.isNullOrEmpty()) { - return method - } - } - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentation(method) - if (result != null) { - return result - } - } - - return null - } - - private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair? { - val superMethods = current.findSuperMethods() - val mappedFilteredTags = superMethods.map { - it to it.docComment?.tags?.filter { it.name == elementToExpand.name } - } - - for ((method, tags) in mappedFilteredTags) { - tags ?: continue - for (tag in tags) { - val (tagSubject, elementSubject) = when (tag.name) { - "throws" -> { - // match class names only for throws, ignore possibly fully qualified path - // TODO: Always match exactly here - tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last() - } - else -> { - tag.getSubjectName() to elementToExpand.getSubjectName() - } - } - - if (tagSubject == elementSubject) { - return method to tag - } - } - } - - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method) - if (result != null) { - return result - } - } - return null - } - -} diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt deleted file mode 100644 index 7c9f2d15..00000000 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ /dev/null @@ -1,188 +0,0 @@ -package org.jetbrains.dokka.Kotlin - -import com.google.inject.Inject -import com.intellij.psi.PsiDocCommentOwner -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.util.PsiTreeUtil -import org.intellij.markdown.parser.LinkMap -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.api.KDoc -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.annotations.argumentValue -import org.jetbrains.kotlin.resolve.constants.StringValue -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement - -class DescriptorDocumentationParser -@Inject constructor( - val options: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val sampleService: SampleProcessingService, - val signatureProvider: KotlinElementSignatureProvider, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) { - fun parseDocumentation( - descriptor: DeclarationDescriptor, - inline: Boolean = false, - isDefaultNoArgConstructor: Boolean = false - ): Content = - parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first - - fun parseDocumentationAndDetails( - descriptor: DeclarationDescriptor, - inline: Boolean = false, - isDefaultNoArgConstructor: Boolean = false - ): Pair Unit> { - if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) { - return parseJavadoc(descriptor) - } - - val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor) - if (kdoc == null) { - if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() && - descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor && - descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) { - logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}") - } - return Content.Empty to { node -> } - } - - val contextDescriptor = - (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration) - ?.takeIf { it != descriptor.original.sourcePsi() } - ?.resolveToDescriptorIfAny() - ?: descriptor - - var kdocText = if (isDefaultNoArgConstructor) { - getConstructorTagContent(descriptor) ?: kdoc.getContent() - } else kdoc.getContent() - - // workaround for code fence parsing problem in IJ markdown parser - if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) { - kdocText += "\n" - } - val tree = parseMarkdown(kdocText) - val linkMap = LinkMap.buildLinkMap(tree.node, kdocText) - val content = buildContent( - tree, - LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }, - inline - ) - if (kdoc is KDocSection) { - val tags = kdoc.getTags() - tags.forEach { - when (it.knownTag) { - KDocKnownTag.SAMPLE -> - content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it)) - KDocKnownTag.SEE -> - content.addTagToSeeAlso(contextDescriptor, it) - else -> { - val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) - val sectionContent = it.getContent() - val markdownNode = parseMarkdown(sectionContent) - buildInlineContentTo( - markdownNode, - section, - LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }) - } - } - } - } - return content to { node -> } - } - - private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? { - return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag( - KDocKnownTag.CONSTRUCTOR - )?.getContent() - } - - - private fun DeclarationDescriptor.isSuppressWarning(): Boolean { - val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!)) - return if (suppressAnnotation != null) { - @Suppress("UNCHECKED_CAST") - (suppressAnnotation.argumentValue("names")?.value as List).any { it.value == "NOT_DOCUMENTED" } - } else containingDeclaration?.isSuppressWarning() ?: false - } - - /** - * Special case for generating stdlib documentation (the Any class to which the override chain will resolve - * is not the same one as the Any class included in the source scope). - */ - fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? { - if (descriptor !is CallableMemberDescriptor) { - return null - } - val name = descriptor.name.asString() - if (name == "equals" || name == "hashCode" || name == "toString") { - var deepestDescriptor: CallableMemberDescriptor = descriptor - while (!deepestDescriptor.overriddenDescriptors.isEmpty()) { - deepestDescriptor = deepestDescriptor.overriddenDescriptors.first() - } - if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") { - val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors( - FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE - ) - anyClassDescriptors.forEach { - val anyMethod = (it as ClassDescriptor).getMemberScope(listOf()) - .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) { it == descriptor.name } - .single() - val kdoc = anyMethod.findKDoc() - if (kdoc != null) { - return kdoc - } - } - } - } - return null - } - - fun parseJavadoc(descriptor: DeclarationDescriptor): Pair Unit> { - val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser( - refGraph, - logger, - signatureProvider, - externalDocumentationLinkResolver - ).parseDocumentation(psi as PsiNamedElement) - return parseResult.content to { node -> } - } - return Content.Empty to { node -> } - } - - fun KDocSection.getTags(): Array = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf() - - private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) { - val subjectName = seeTag.getSubjectName() - if (subjectName != null) { - val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null) - val link = linkResolver.resolveContentLink(descriptor, subjectName) - link.append(ContentText(subjectName)) - val para = ContentParagraph() - para.append(link) - seeSection.append(para) - } - } - -} diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt deleted file mode 100644 index 6d258564..00000000 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ /dev/null @@ -1,534 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.psi.PsiJavaFile -import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes -import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny -import org.jetbrains.kotlin.types.typeUtil.isTypeParameter -import org.jetbrains.kotlin.types.typeUtil.supertypes -import org.jetbrains.kotlin.util.supertypesWithAny -import kotlin.reflect.KClass -import com.google.inject.name.Named as GuiceNamed - -private fun isExtensionForExternalClass( - extensionFunctionDescriptor: DeclarationDescriptor, - extensionReceiverDescriptor: DeclarationDescriptor, - allFqNames: Collection -): Boolean { - val extensionFunctionPackage = - DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java) - val extensionReceiverPackage = - DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java) - return extensionFunctionPackage != null && extensionReceiverPackage != null && - extensionFunctionPackage.fqName != extensionReceiverPackage.fqName && - extensionReceiverPackage.fqName !in allFqNames -} - -interface PackageDocumentationBuilder { - fun buildPackageDocumentation( - documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNodes.Package, - declarations: List, - allFqNames: Collection - ) -} - -interface DefaultPlatformsProvider { - fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List -} - -val ignoredSupertypes = setOf( - "kotlin.Annotation", "kotlin.Enum", "kotlin.Any" -) - -class DocumentationBuilder -@Inject constructor( - val resolutionFacade: DokkaResolutionFacade, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger -) { - - private fun DocumentationNodes.Class.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) { - val unwrappedType = superType.unwrap() - if (unwrappedType is AbbreviatedType) { - appendSupertype(descriptor, unwrappedType.abbreviation, backref) - } else { - appendType(unwrappedType, descriptor) - } - } - - private fun DocumentationNodes.Class.appendType( - kotlinType: KotlinType?, - descriptor: ClassDescriptor? - ) { - if (kotlinType == null) - return - (kotlinType.unwrap() as? AbbreviatedType)?.let { - return appendType(it.abbreviation, descriptor) - } - - if (kotlinType.isDynamic()) { - append(kind.createNode("dynamic", descriptor), RefKind.Detail) - return - } - - val classifierDescriptor = kotlinType.constructor.declarationDescriptor - val name = when (classifierDescriptor) { - is ClassDescriptor -> { - if (classifierDescriptor.isCompanionObject) { - classifierDescriptor.containingDeclaration.name.asString() + - "." + classifierDescriptor.name.asString() - } else { - classifierDescriptor.name.asString() - } - } - is Named -> classifierDescriptor.name.asString() - else -> "" - } - val node = kind.createNode(name, descriptor) - - append(node, RefKind.Detail) - for (typeArgument in kotlinType.arguments) { - node.appendProjection(typeArgument, null) - } - } - - fun DocumentationNode<*>.appendChild(descriptor: DeclarationDescriptor) { - if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) { - val node = descriptor.build() - append(node, kind) - } - } - - fun DocumentationNode.appendMember(des