diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Formats/FormatDescriptor.kt | 4 | ||||
-rw-r--r-- | src/Formats/StandardFormats.kt | 12 | ||||
-rw-r--r-- | src/Generation/FileGenerator.kt | 4 | ||||
-rw-r--r-- | src/Java/JavaDocumentationBuilder.kt | 10 | ||||
-rw-r--r-- | src/Kotlin/DocumentationBuilder.kt | 145 | ||||
-rw-r--r-- | src/Kotlin/KotlinAsJavaDocumentationBuilder.kt | 27 | ||||
-rw-r--r-- | src/Utilities/GuiceModule.kt | 7 | ||||
-rw-r--r-- | src/main.kt | 24 |
8 files changed, 162 insertions, 71 deletions
diff --git a/src/Formats/FormatDescriptor.kt b/src/Formats/FormatDescriptor.kt index beff730f..cc12dfb9 100644 --- a/src/Formats/FormatDescriptor.kt +++ b/src/Formats/FormatDescriptor.kt @@ -3,9 +3,11 @@ package org.jetbrains.dokka.Formats import org.jetbrains.dokka.FormatService import org.jetbrains.dokka.Generator import org.jetbrains.dokka.OutlineFormatService +import org.jetbrains.dokka.PackageDocumentationBuilder public interface FormatDescriptor { val formatServiceClass: Class<out FormatService>? val outlineServiceClass: Class<out OutlineFormatService>? val generatorServiceClass: Class<out Generator> -}
\ No newline at end of file + val packageDocumentationBuilderServiceClass: Class<out PackageDocumentationBuilder>? +} diff --git a/src/Formats/StandardFormats.kt b/src/Formats/StandardFormats.kt index 658735c0..12b5d85d 100644 --- a/src/Formats/StandardFormats.kt +++ b/src/Formats/StandardFormats.kt @@ -11,6 +11,9 @@ class HtmlFormatDescriptor : FormatDescriptor { override val generatorServiceClass: Class<out Generator> get() = FileGenerator::class.java + + override val packageDocumentationBuilderServiceClass: Class<out PackageDocumentationBuilder>? + get() = null } class KotlinWebsiteFormatDescriptor : FormatDescriptor { @@ -22,6 +25,9 @@ class KotlinWebsiteFormatDescriptor : FormatDescriptor { override val generatorServiceClass: Class<out Generator> get() = FileGenerator::class.java + + override val packageDocumentationBuilderServiceClass: Class<out PackageDocumentationBuilder>? + get() = null } class JekyllFormatDescriptor : FormatDescriptor { @@ -33,6 +39,9 @@ class JekyllFormatDescriptor : FormatDescriptor { override val generatorServiceClass: Class<out Generator> get() = FileGenerator::class.java + + override val packageDocumentationBuilderServiceClass: Class<out PackageDocumentationBuilder>? + get() = null } class MarkdownFormatDescriptor : FormatDescriptor { @@ -44,4 +53,7 @@ class MarkdownFormatDescriptor : FormatDescriptor { override val generatorServiceClass: Class<out Generator> get() = FileGenerator::class.java + + override val packageDocumentationBuilderServiceClass: Class<out PackageDocumentationBuilder>? + get() = null } diff --git a/src/Generation/FileGenerator.kt b/src/Generation/FileGenerator.kt index c4025088..810038fa 100644 --- a/src/Generation/FileGenerator.kt +++ b/src/Generation/FileGenerator.kt @@ -6,10 +6,10 @@ import java.io.FileOutputStream import java.io.IOException import java.io.OutputStreamWriter -public class FileGenerator @Inject constructor(val locationService: FileLocationService, - val formatService: FormatService) : Generator { +public class FileGenerator @Inject constructor(val locationService: FileLocationService) : Generator { @set:Inject(optional = true) var outlineService: OutlineFormatService? = null + @set:Inject(optional = true) lateinit var formatService: FormatService override fun buildPages(nodes: Iterable<DocumentationNode>) { val specificLocationService = locationService.withExtension(formatService.extension) diff --git a/src/Java/JavaDocumentationBuilder.kt b/src/Java/JavaDocumentationBuilder.kt index f3cedc07..660ac4a8 100644 --- a/src/Java/JavaDocumentationBuilder.kt +++ b/src/Java/JavaDocumentationBuilder.kt @@ -15,7 +15,11 @@ public class JavaDocumentationBuilder(private val options: DocumentationOptions, return } val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap()) - packageNode.appendChildren(file.classes) { build() } + appendClasses(packageNode, file.classes) + } + + fun appendClasses(packageNode: DocumentationNode, classes: Array<PsiClass>) { + packageNode.appendChildren(classes) { build() } } data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) @@ -47,9 +51,9 @@ public class JavaDocumentationBuilder(private val options: DocumentationOptions, private fun PsiDocTag.contentElements(): Iterable<PsiElement> { val tagValueElements = children - .dropWhile { it.node.elementType == JavaDocTokenType.DOC_TAG_NAME } + .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } + .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements } diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index 2d3105ac..375ca4bd 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka +import com.intellij.openapi.project.Project import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.dokka.DocumentationNode.Kind @@ -43,6 +44,14 @@ private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: Decla return package1 != null && package2 != null && package1.fqName == package2.fqName } +interface PackageDocumentationBuilder { + fun buildPackageDocumentation(project: Project, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) +} + class DocumentationBuilder(val resolutionFacade: ResolutionFacade, val session: ResolveSession, val options: DocumentationOptions, @@ -124,10 +133,6 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, return null } - fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { - DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" - } || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) - fun DeclarationDescriptor.signature(): String = when(this) { is ClassDescriptor, is PackageFragmentDescriptor -> DescriptorUtils.getFqName(this).asString() is PropertyDescriptor -> containingDeclaration.signature() + "#" + name + receiverSignature() @@ -353,17 +358,6 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, } } - private fun AnnotationDescriptor.isDocumented(): Boolean { - if (source.getPsi() != null && mustBeDocumented()) return true - val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() - return annotationClassName == "kotlin.Extension" - } - - fun AnnotationDescriptor.mustBeDocumented(): Boolean { - val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false - return annotationClass.isDocumentedAnnotation() - } - fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach { @@ -400,12 +394,6 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, (!options.skipDeprecated || !isDeprecated()) } - fun DeclarationDescriptor.isDocumentationSuppressed(): Boolean { - val doc = KDocFinder.findKDoc(this) - return doc is KDocSection && doc.findTagByName("suppress") != null - } - - fun DocumentationNode.appendMembers(descriptors: Iterable<DeclarationDescriptor>) { descriptors.forEach { descriptor -> if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { @@ -427,40 +415,18 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, } } - fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor, - externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode { - if (descriptor is CallableMemberDescriptor) { - val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() - if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor) && - !ErrorUtils.isError(extensionClassDescriptor)) { - val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) - return externalClassNodes.getOrPut(fqName, { - val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass) - append(newNode, DocumentationReference.Kind.Member) - newNode - }) - } - } - return this - } - fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>, - packageContent: Map<String, Content>) { - val descriptors = hashMapOf<String, List<DeclarationDescriptor>>() - for ((name, parts) in fragments.groupBy { it.fqName }) { - descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() }) - } - for ((packageName, declarations) in descriptors) { - if (options.skipEmptyPackages && declarations.none { it.isDocumented()}) continue + packageContent: Map<String, Content>, + packageDocumentationBuilder: PackageDocumentationBuilder = KotlinPackageDocumentationBuilder()) { + val allFqNames = fragments.map { it.fqName }.distinct() + + for (packageName in allFqNames) { + val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getAllDescriptors() } + + if (options.skipEmptyPackages && declarations.none { it.isDocumented() }) continue logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(packageName, packageContent) - val externalClassNodes = hashMapOf<FqName, DocumentationNode>() - declarations.forEach { descriptor -> - if (descriptor.isDocumented()) { - val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes) - parent.appendChild(descriptor, DocumentationReference.Kind.Member) - } - } + val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent) + packageDocumentationBuilder.buildPackageDocumentation(resolutionFacade.project, packageName, packageNode, declarations, options, refGraph, logger) } } @@ -526,15 +492,6 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false } - fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - val type = extensionReceiver.type - return type.constructor.declarationDescriptor as? ClassDescriptor - } - return null - } - fun FunctionDescriptor.build(): DocumentationNode { if (ErrorUtils.containsErrorType(this)) { logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}") @@ -707,4 +664,68 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) } } + + inner class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { + override fun buildPackageDocumentation(project: Project, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + options: DocumentationOptions, + refGraph: NodeReferenceGraph, logger: DokkaLogger) { + val externalClassNodes = hashMapOf<FqName, DocumentationNode>() + declarations.forEach { descriptor -> + if (descriptor.isDocumented()) { + val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes) + parent.appendChild(descriptor, DocumentationReference.Kind.Member) + } + } + } + } +} + +private fun AnnotationDescriptor.isDocumented(): Boolean { + if (source.getPsi() != null && mustBeDocumented()) return true + val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() + return annotationClassName == "kotlin.Extension" +} + +fun AnnotationDescriptor.mustBeDocumented(): Boolean { + val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false + return annotationClass.isDocumentedAnnotation() +} + +fun DeclarationDescriptor.isDocumentationSuppressed(): Boolean { + val doc = KDocFinder.findKDoc(this) + return doc is KDocSection && doc.findTagByName("suppress") != null +} + +fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { + DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" +} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) + +fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor, + externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode { + if (descriptor is CallableMemberDescriptor) { + val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() + if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor) && + !ErrorUtils.isError(extensionClassDescriptor)) { + val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) + return externalClassNodes.getOrPut(fqName, { + val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass) + append(newNode, DocumentationReference.Kind.Member) + newNode + }) + } + } + return this } + +fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { + val extensionReceiver = extensionReceiverParameter + if (extensionReceiver != null) { + val type = extensionReceiver.type + return type.constructor.declarationDescriptor as? ClassDescriptor + } + return null +} + diff --git a/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt new file mode 100644 index 00000000..ae295769 --- /dev/null +++ b/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka + +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import org.jetbrains.kotlin.asJava.KotlinLightElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.name.FqName + +class KotlinAsJavaDocumentationBuilder() : PackageDocumentationBuilder { + override fun buildPackageDocumentation(project: Project, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + options: DocumentationOptions, + refGraph: NodeReferenceGraph, + logger: DokkaLogger) { + val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString()) + if (psiPackage == null) { + logger.error("Cannot find Java package by qualified name: ${packageName.asString()}") + return + } + val javaDocumentationBuilder = JavaDocumentationBuilder(options, refGraph) + psiPackage.classes.filter { it is KotlinLightElement<*, *> }.forEach { + javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it)) + } + } +} diff --git a/src/Utilities/GuiceModule.kt b/src/Utilities/GuiceModule.kt index 855d70b6..e852ae19 100644 --- a/src/Utilities/GuiceModule.kt +++ b/src/Utilities/GuiceModule.kt @@ -4,6 +4,7 @@ import com.google.inject.Binder import com.google.inject.Module import com.google.inject.Provider import com.google.inject.name.Names +import com.google.inject.util.Providers import org.jetbrains.dokka.* import org.jetbrains.dokka.Formats.FormatDescriptor import java.io.File @@ -40,6 +41,12 @@ class GuiceModule(val config: DokkaGenerator) : Module { descriptor.formatServiceClass?.let { clazz -> binder.bind(FormatService::class.java).to(clazz) } + if (descriptor.packageDocumentationBuilderServiceClass != null) { + binder.bind(PackageDocumentationBuilder::class.java).to(descriptor.packageDocumentationBuilderServiceClass) + } else { + binder.bind(PackageDocumentationBuilder::class.java).toProvider(Providers.of(null)) + } + binder.bind(Generator::class.java).to(descriptor.generatorServiceClass) } diff --git a/src/main.kt b/src/main.kt index 1d2442f2..d0e693e9 100644 --- a/src/main.kt +++ b/src/main.kt @@ -17,6 +17,8 @@ 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.CommonConfigurationKeys +import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzerForTopLevel +import org.jetbrains.kotlin.resolve.TopDownAnalysisMode import org.jetbrains.kotlin.utils.PathUtil import java.io.File import kotlin.util.measureTimeMillis @@ -153,14 +155,21 @@ class DokkaGenerator(val logger: DokkaLogger, val startAnalyse = System.currentTimeMillis() val options = DocumentationOptions(false, sourceLinks = sourceLinks, skipDeprecated = skipDeprecated) - val documentation = buildDocumentationModule(environment, moduleName, options, includes, { isSample(it) }, logger) + + val injector = Guice.createInjector(GuiceModule(this)) + val generator = injector.getInstance(Generator::class.java) + + val packageDocumentationBuilder = injector.getInstance(PackageDocumentationBuilder::class.java) + + val documentation = buildDocumentationModule(environment, moduleName, options, includes, { isSample(it) }, + packageDocumentationBuilder, logger) val timeAnalyse = System.currentTimeMillis() - startAnalyse logger.info("done in ${timeAnalyse / 1000} secs") val timeBuild = measureTimeMillis { logger.info("Generating pages... ") - Guice.createInjector(GuiceModule(this)).getInstance(Generator::class.java).buildAll(documentation) + generator.buildAll(documentation) } logger.info("done in ${timeBuild / 1000} secs") @@ -196,9 +205,13 @@ fun buildDocumentationModule(environment: AnalysisEnvironment, options: DocumentationOptions, includes: List<String> = listOf(), filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, + packageDocumentationBuilder: PackageDocumentationBuilder? = null, logger: DokkaLogger): DocumentationModule { val documentation = environment.withContext { environment, resolutionFacade, session -> val fragmentFiles = environment.getSourceFiles().filter(filesToDocumentFilter) + val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzerForTopLevel::class.java) + analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) + val fragments = fragmentFiles.map { session.getPackageFragment(it.packageFqName) }.filterNotNull().distinct() val refGraph = NodeReferenceGraph() @@ -210,7 +223,12 @@ fun buildDocumentationModule(environment: AnalysisEnvironment, val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent) with(documentationBuilder) { - documentationModule.appendFragments(fragments, packageDocs.packageContent) + if (packageDocumentationBuilder != null) { + documentationModule.appendFragments(fragments, packageDocs.packageContent, packageDocumentationBuilder) + } + else { + documentationModule.appendFragments(fragments, packageDocs.packageContent) + } } val javaFiles = environment.getJavaSourceFiles().filter(filesToDocumentFilter) |