From 8883839b8796589a1fa02b3dca5d1aae172b5c56 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 13 Apr 2018 19:47:07 +0300 Subject: [backport] Fix problems in as-java java-layout-html mode Extract common part of LanguageServices & other improvements Original: 853262e --- core/src/main/kotlin/Java/JavadocParser.kt | 43 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index c25f5813..db61a00a 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -20,8 +20,10 @@ interface JavaDocumentationParser { fun parseDocumentation(element: PsiNamedElement): JavadocParseResult } -class JavadocParser(private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger) : 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 @@ -31,7 +33,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, result.append(para) para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) docComment.tags.forEach { tag -> - when(tag.name) { + when (tag.name) { "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content() @@ -50,9 +52,9 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, 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 } + .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 } @@ -83,7 +85,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } } - private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { + private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { "p" -> ContentParagraph() "b", "strong" -> ContentStrong() "i", "em" -> ContentEmphasis() @@ -99,28 +101,26 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } private fun createLink(element: Element): ContentBlock { - val docref = element.attr("docref") - if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)}) + if (element.hasAttr("docref")) { + val docref = element.attr("docref") + return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) } - val href = element.attr("href") - if (href != null) { - return ContentExternalLink(href) + return if (element.hasAttr("href")) { + val href = element.attr("href") + ContentExternalLink(href) } else { - return ContentBlock() + ContentBlock() } } private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() - if (linkElement == null) { - return - } + val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) val linkSignature = resolveLink(linkElement) val text = ContentText(linkElement.text) if (linkSignature != null) { - val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)}) + val linkNode = + ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) linkNode.append(text) seeSection.append(linkNode) } else { @@ -136,8 +136,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text val link = "${labelText.htmlEscape()}" if (tag.name == "link") "$link" else link - } - else if (valueElement != null) { + } else if (valueElement != null) { valueElement.text } else { "" @@ -153,7 +152,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } private fun resolveLink(valueElement: PsiElement?): String? { val target = valueElement?.reference?.resolve() -- cgit From a3f16fd75c200020465f79563ca58b2833236865 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 24 Apr 2018 20:35:42 +0300 Subject: [backport] Stabilize signatures to fix linking from Java to Kotlin Basically there is 2 ways to get signature, from PSI and from descriptors, and there should be only one way in one session Original: adc09f2 --- .../main/kotlin/Analysis/AnalysisEnvironment.kt | 3 +- .../main/kotlin/Analysis/JavaResolveExtension.kt | 131 +++++++++++++++++++++ core/src/main/kotlin/Formats/AnalysisComponents.kt | 14 +-- .../kotlin/Java/JavaPsiDocumentationBuilder.kt | 6 +- core/src/main/kotlin/Java/JavadocParser.kt | 5 +- .../main/kotlin/Kotlin/DeclarationLinkResolver.kt | 7 +- .../kotlin/Kotlin/DescriptorDocumentationParser.kt | 6 +- .../src/main/kotlin/Kotlin/DocumentationBuilder.kt | 23 ++-- .../KotlinAsJavaDescriptorSignatureProvider.kt | 22 ---- .../Kotlin/KotlinAsJavaElementSignatureProvider.kt | 25 ++++ .../Kotlin/KotlinDescriptorSignatureProvider.kt | 9 -- .../Kotlin/KotlinElementSignatureProvider.kt | 31 +++++ .../kotlin/Model/DescriptorSignatureProvider.kt | 7 -- core/src/main/kotlin/Model/DocumentationNode.kt | 7 +- .../main/kotlin/Model/ElementSignatureProvider.kt | 9 ++ core/src/main/kotlin/Utilities/DokkaModules.kt | 3 - 16 files changed, 230 insertions(+), 78 deletions(-) create mode 100644 core/src/main/kotlin/Analysis/JavaResolveExtension.kt delete mode 100644 core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt create mode 100644 core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt delete mode 100644 core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt create mode 100644 core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt delete mode 100644 core/src/main/kotlin/Model/DescriptorSignatureProvider.kt create mode 100644 core/src/main/kotlin/Model/ElementSignatureProvider.kt (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt index b2e4b490..6854b323 100644 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.cli.jvm.config.* import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.container.getService +import org.jetbrains.kotlin.container.tryGetService import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor @@ -250,7 +251,7 @@ class DokkaResolutionFacade(override val project: Project, } override fun tryGetFrontendService(element: PsiElement, serviceClass: Class): T? { - return null + return resolverForModule.componentProvider.tryGetService(serviceClass) } override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor { diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt new file mode 100644 index 00000000..f8992d45 --- /dev/null +++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("JavaResolutionUtils") + +package org.jetbrains.dokka + +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.java.structure.impl.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.psiUtil.parameterIndex +import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope + +// TODO: Remove that file + +@JvmOverloads +fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + val method = originalElement as? PsiMethod ?: return null + if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null + val resolver = method.getJavaDescriptorResolver(resolutionFacade) + return when { + method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method)) + else -> resolver?.resolveMethod(JavaMethodImpl(method)) + } +} + +@JvmOverloads +fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? { + val psiClass = originalElement as? PsiClass ?: return null + return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass)) +} + +@JvmOverloads +fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? { + val field = originalElement as? PsiField ?: return null + return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field)) +} + +@JvmOverloads +fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + return when (this) { + is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade) + is PsiClass -> getJavaClassDescriptor(resolutionFacade) + is PsiMethod -> getJavaMethodDescriptor(resolutionFacade) + is PsiField -> getJavaFieldDescriptor(resolutionFacade) + else -> null + } +} + +@JvmOverloads +fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + val callable = unwrapped + return when (callable) { + is PsiMember -> getJavaMemberDescriptor(resolutionFacade) + is KtDeclaration -> { + val descriptor = resolutionFacade.resolveToDescriptor(callable) + if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor + } + else -> null + } +} + +private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? { + return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java) +} + +private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? { + return getContainingScope(method) + ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES) + ?.filterIsInstance() + ?.findByJavaElement(method) +} + +private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? { + return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor) +} + +private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? { + return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field) +} + +private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? { + val containingClass = resolveClass(member.containingClass) + return if (member.isStatic) + containingClass?.staticScope + else + containingClass?.defaultType?.memberScope +} + +private fun Collection.findByJavaElement(javaElement: JavaElement): T? { + return firstOrNull { member -> + val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement + when { + memberJavaElement == javaElement -> + true + memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> -> + memberJavaElement.psi.isEquivalentTo(javaElement.psi) + else -> + false + } + } +} + +fun PsiElement.javaResolutionFacade() = + KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform) diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt index c4d97dbb..d78d4a0c 100644 --- a/core/src/main/kotlin/Formats/AnalysisComponents.kt +++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt @@ -2,9 +2,9 @@ package org.jetbrains.dokka.Formats import com.google.inject.Binder import org.jetbrains.dokka.* -import org.jetbrains.dokka.Kotlin.KotlinAsJavaDescriptorSignatureProvider -import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider -import org.jetbrains.dokka.Model.DescriptorSignatureProvider +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 @@ -16,12 +16,12 @@ interface DefaultAnalysisComponentServices { val packageDocumentationBuilderClass: KClass val javaDocumentationBuilderClass: KClass val sampleProcessingService: KClass - val descriptorSignatureProvider: KClass + val elementSignatureProvider: KClass } interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { override fun configureAnalysis(binder: Binder): Unit = with(binder) { - bind() toType descriptorSignatureProvider + bind() toType elementSignatureProvider bind() toType packageDocumentationBuilderClass bind() toType javaDocumentationBuilderClass bind() toType sampleProcessingService @@ -33,7 +33,7 @@ object KotlinAsJava : DefaultAnalysisComponentServices { override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class override val sampleProcessingService = DefaultSampleProcessingService::class - override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::class + override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class } @@ -41,5 +41,5 @@ object KotlinAsKotlin : DefaultAnalysisComponentServices { override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class override val sampleProcessingService = DefaultSampleProcessingService::class - override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class + override val elementSignatureProvider = KotlinElementSignatureProvider::class } \ No newline at end of file diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index cf2b0514..646096e5 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -45,10 +45,10 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) { + @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger, signatureProvider: ElementSignatureProvider) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger) + this.docParser = JavadocParser(refGraph, logger, signatureProvider) } constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { @@ -61,7 +61,7 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { if (skipFile(file) || file.classes.all { skipElement(it) }) { return } - val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap(), refGraph) + val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) appendClasses(packageNode, file.classes) } diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index db61a00a..365ae298 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -22,7 +22,8 @@ interface JavaDocumentationParser { class JavadocParser( private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger + private val logger: DokkaLogger, + private val signatureProvider: ElementSignatureProvider ) : JavaDocumentationParser { override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment @@ -157,7 +158,7 @@ class JavadocParser( private fun resolveLink(valueElement: PsiElement?): String? { val target = valueElement?.reference?.resolve() if (target != null) { - return getSignature(target) + return signatureProvider.signature(target) } return null } diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt index ec474900..d73bef4a 100644 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt @@ -1,13 +1,10 @@ package org.jetbrains.dokka import com.google.inject.Inject -import org.jetbrains.dokka.Model.DescriptorSignatureProvider import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi -import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi class DeclarationLinkResolver @Inject constructor(val resolutionFacade: DokkaResolutionFacade, @@ -15,7 +12,7 @@ class DeclarationLinkResolver val logger: DokkaLogger, val options: DocumentationOptions, val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, - val descriptorSignatureProvider: DescriptorSignatureProvider) { + val elementSignatureProvider: ElementSignatureProvider) { fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { @@ -34,7 +31,7 @@ class DeclarationLinkResolver if (externalHref != null) { return ContentExternalLink(externalHref) } - val signature = descriptorSignatureProvider.signature(symbol) + val signature = elementSignatureProvider.signature(symbol) val referencedAt = fromDescriptor.signatureWithSourceLocation() return ContentNodeLazyLink(href, { -> diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt index 6e44df74..5f1118e8 100644 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt @@ -33,7 +33,9 @@ class DescriptorDocumentationParser val linkResolver: DeclarationLinkResolver, val resolutionFacade: DokkaResolutionFacade, val refGraph: NodeReferenceGraph, - val sampleService: SampleProcessingService) + val sampleService: SampleProcessingService, + val signatureProvider: KotlinElementSignatureProvider + ) { fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content = parseDocumentationAndDetails(descriptor, inline).first @@ -129,7 +131,7 @@ class DescriptorDocumentationParser fun parseJavadoc(descriptor: DeclarationDescriptor): Pair Unit> { val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement) + val parseResult = JavadocParser(refGraph, logger, signatureProvider).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 d7d30ebb..05bd2cc7 100644 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.idea.util.toFuzzyType import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -29,7 +28,6 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors -import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.resolve.source.PsiSourceElement @@ -192,14 +190,16 @@ class DocumentationBuilder appendTextNode(modifier, NodeKind.Modifier) } - fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType) { + fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) { val unwrappedType = superType.unwrap() if (unwrappedType is AbbreviatedType) { - appendSupertype(descriptor, unwrappedType.abbreviation) + appendSupertype(descriptor, unwrappedType.abbreviation, backref) } else { appendType(unwrappedType, NodeKind.Supertype) val superclass = unwrappedType.constructor.declarationDescriptor - link(superclass, descriptor, RefKind.Inheritor) + if (backref) { + link(superclass, descriptor, RefKind.Inheritor) + } link(descriptor, superclass, RefKind.Superclass) } } @@ -457,7 +457,7 @@ class DocumentationBuilder if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent, this@DocumentationBuilder.refGraph) + val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph) packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode, declarations, allFqNames) } @@ -646,7 +646,7 @@ class DocumentationBuilder val node = nodeForDescriptor(this, kind, external) register(this, node) typeConstructor.supertypes.forEach { - node.appendSupertype(this, it) + node.appendSupertype(this, it, !external) } if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) @@ -997,16 +997,11 @@ class KotlinJavaDocumentationBuilder val logger: DokkaLogger) : JavaDocumentationBuilder { override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) { val classDescriptors = file.classes.map { - val javaDescriptorResolver = resolutionFacade.getFrontendService(JavaDescriptorResolver::class.java) - - javaDescriptorResolver.resolveClass(JavaClassImpl(it)) ?: run { - logger.warn("Cannot find descriptor for Java class ${it.qualifiedName}") - null - } + it.getJavaClassDescriptor(resolutionFacade) } if (classDescriptors.any { it != null && it.isDocumented(options) }) { - val packageNode = module.findOrCreatePackageNode(file.packageName, packageContent, documentationBuilder.refGraph) + val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph) for (descriptor in classDescriptors.filterNotNull()) { with(documentationBuilder) { diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt deleted file mode 100644 index a3be658e..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.jetbrains.dokka.Kotlin - -import org.jetbrains.dokka.Model.DescriptorSignatureProvider -import org.jetbrains.dokka.getSignature -import org.jetbrains.dokka.sourcePsi -import org.jetbrains.kotlin.asJava.toLightElements -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtElement - -class KotlinAsJavaDescriptorSignatureProvider : DescriptorSignatureProvider { - override fun signature(forDesc: DeclarationDescriptor): String { - val sourcePsi = forDesc.sourcePsi() - val javaLikePsi = if (sourcePsi is KtElement) { - sourcePsi.toLightElements().firstOrNull() - } else { - sourcePsi - } - - return getSignature(javaLikePsi) ?: - "not implemented for $forDesc with psi: $sourcePsi" - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt new file mode 100644 index 00000000..20ea179e --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtElement + +class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider { + + private fun PsiElement.javaLikePsi() = when { + this is KtElement -> toLightElements().firstOrNull() + else -> this + } + + override fun signature(forPsi: PsiElement): String { + return getSignature(forPsi.javaLikePsi()) ?: + "not implemented for $forPsi" + } + + override fun signature(forDesc: DeclarationDescriptor): String { + val sourcePsi = forDesc.sourcePsi() + return getSignature(sourcePsi?.javaLikePsi()) ?: + "not implemented for $forDesc with psi: $sourcePsi" + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt deleted file mode 100644 index 7ecd0389..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka.Kotlin - -import org.jetbrains.dokka.Model.DescriptorSignatureProvider -import org.jetbrains.dokka.signature -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor - -class KotlinDescriptorSignatureProvider : DescriptorSignatureProvider { - override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() -} \ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt new file mode 100644 index 00000000..4f788634 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.resolve.BindingContext +import javax.inject.Inject + +class KotlinElementSignatureProvider @Inject constructor( + val resolutionFacade: DokkaResolutionFacade +) : ElementSignatureProvider { + override fun signature(forPsi: PsiElement): String { + + val desc = when (forPsi) { + is KtLightElement<*, *> -> return signature(forPsi.kotlinOrigin!!) + is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) + else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] + } + + if (desc == null && (forPsi as? PsiNameIdentifierOwner)?.name == "remove") { + (forPsi as? PsiMember)?.getJavaOrKotlinMemberDescriptor(resolutionFacade) + } + + return desc?.let { signature(it) } + ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" } + } + + override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() +} \ No newline at end of file diff --git a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt b/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt deleted file mode 100644 index 85584e3c..00000000 --- a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.jetbrains.dokka.Model - -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor - -interface DescriptorSignatureProvider { - fun signature(forDesc: DeclarationDescriptor): String -} \ No newline at end of file diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index 84501d2b..4739d736 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -176,16 +176,17 @@ val DocumentationNode.path: List return parent.path + this } -fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map, refGraph: NodeReferenceGraph): DocumentationNode { - val existingNode = members(NodeKind.Package).firstOrNull { it.name == packageName } +fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map, refGraph: NodeReferenceGraph): DocumentationNode { + val existingNode = refGraph.lookup(packageName) if (existingNode != null) { return existingNode } val newNode = DocumentationNode(packageName, packageContent.getOrElse(packageName) { Content.Empty }, NodeKind.Package) - append(newNode, RefKind.Member) + refGraph.register(packageName, newNode) + module?.append(newNode, RefKind.Member) return newNode } diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt new file mode 100644 index 00000000..e8fdde6e --- /dev/null +++ b/core/src/main/kotlin/Model/ElementSignatureProvider.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor + +interface ElementSignatureProvider { + fun signature(forDesc: DeclarationDescriptor): String + fun signature(forPsi: PsiElement): String +} \ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt index 36704918..907d5ca7 100644 --- a/core/src/main/kotlin/Utilities/DokkaModules.kt +++ b/core/src/main/kotlin/Utilities/DokkaModules.kt @@ -2,14 +2,11 @@ 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.binder.AnnotatedBindingBuilder import com.google.inject.name.Names import org.jetbrains.dokka.* import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Model.DescriptorSignatureProvider -import org.jetbrains.dokka.Samples.SampleProcessingService import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import java.io.File import kotlin.reflect.KClass -- cgit From 57a6bb55ddafbde4eab7c1c4344fff3ad3f0fe1f Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 27 Apr 2018 18:05:35 +0300 Subject: [backport] KT-24042: Fix structure of content paragraphs in Javadoc parser Original: edc0581 --- core/src/main/kotlin/Java/JavadocParser.kt | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 365ae298..08ae5354 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -30,9 +30,18 @@ class JavadocParser( if (docComment == null) return JavadocParseResult.Empty val result = MutableContent() var deprecatedContent: Content? = null - val para = ContentParagraph() - result.append(para) - para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + val firstParagraph = ContentParagraph() + firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } + firstParagraph.children.removeAll(paragraphs) + if (!firstParagraph.isEmpty()) { + result.append(firstParagraph) + } + paragraphs.forEach { + result.append(it) + } + val attrs = mutableListOf() + var since: DocumentationNode? = null docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) @@ -70,20 +79,24 @@ class JavadocParser( } val doc = Jsoup.parse(htmlBuilder.toString().trim()) doc.body().childNodes().forEach { - convertHtmlNode(it) + convertHtmlNode(it)?.let { append(it) } } } - private fun ContentBlock.convertHtmlNode(node: Node) { + private fun convertHtmlNode(node: Node): ContentNode? { if (node is TextNode) { - append(ContentText(node.text())) + return ContentText(node.text()) } else if (node is Element) { val childBlock = createBlock(node) node.childNodes().forEach { - childBlock.convertHtmlNode(it) + val child = convertHtmlNode(it) + if (child != null) { + childBlock.append(child) + } } - append(childBlock) + return (childBlock) } + return null } private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { -- cgit From 6cfaf04a6a0a34299d3a39803322b2eea2afbaac Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 27 Apr 2018 19:42:25 +0300 Subject: [backport] KT-24149: Fix resolution of links in Javadoc Original: edc0581 --- core/src/main/kotlin/Java/JavadocParser.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 08ae5354..0869081b 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -1,10 +1,12 @@ package org.jetbrains.dokka import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.PsiDocTag import com.intellij.psi.javadoc.PsiDocTagValue import com.intellij.psi.javadoc.PsiDocToken import com.intellij.psi.javadoc.PsiInlineDocTag +import com.intellij.psi.util.PsiTreeUtil import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -130,11 +132,11 @@ class JavadocParser( private fun MutableContent.convertSeeTag(tag: PsiDocTag) { val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - val linkSignature = resolveLink(linkElement) + val linkSignature = resolveLink(tag.referenceElement()) val text = ContentText(linkElement.text) if (linkSignature != null) { val linkNode = - ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) + ContentNodeLazyLink((tag.valueElement ?: linkElement).text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) linkNode.append(text) seeSection.append(linkNode) } else { @@ -144,7 +146,7 @@ class JavadocParser( private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { "link", "linkplain" -> { - val valueElement = tag.linkElement() + val valueElement = tag.referenceElement() val linkSignature = resolveLink(valueElement) if (linkSignature != null) { val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text @@ -165,6 +167,15 @@ class JavadocParser( 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 } -- cgit From 432868e8732085a0b160466ddbe8262b793b4f95 Mon Sep 17 00:00:00 2001 From: Sean McQuillan Date: Thu, 26 Apr 2018 18:03:04 -0700 Subject: [backport] Support for {@inheritDoc} including grand inheritance and skip levels. Original: f100b73 --- core/src/main/kotlin/Java/JavadocParser.kt | 127 +++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 14 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 0869081b..f88f557d 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -2,11 +2,10 @@ package org.jetbrains.dokka import com.intellij.psi.* import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.PsiDocTag -import com.intellij.psi.javadoc.PsiDocTagValue -import com.intellij.psi.javadoc.PsiDocToken -import com.intellij.psi.javadoc.PsiInlineDocTag +import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.IncorrectOperationException +import com.intellij.util.containers.isNullOrEmpty import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -33,7 +32,7 @@ class JavadocParser( val result = MutableContent() var deprecatedContent: Content? = null val firstParagraph = ContentParagraph() - firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } firstParagraph.children.removeAll(paragraphs) if (!firstParagraph.isEmpty()) { @@ -49,13 +48,13 @@ class JavadocParser( "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements()) + deprecatedContent!!.convertJavadocElements(tag.contentElements(), element) } else -> { val subjectName = tag.getSubjectName() val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - section.convertJavadocElements(tag.contentElements()) + section.convertJavadocElements(tag.contentElements(), element) } } } @@ -70,19 +69,23 @@ class JavadocParser( return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements } - private fun ContentBlock.convertJavadocElements(elements: Iterable) { + private fun ContentBlock.convertJavadocElements(elements: Iterable, element: PsiNamedElement) { + val doc = Jsoup.parse(expandAllForElements(elements, element)) + doc.body().childNodes().forEach { + convertHtmlNode(it)?.let { append(it) } + } + } + + private fun expandAllForElements(elements: Iterable, element: PsiNamedElement): String { val htmlBuilder = StringBuilder() elements.forEach { if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it)) + htmlBuilder.append(convertInlineDocTag(it, element)) } else { htmlBuilder.append(it.text) } } - val doc = Jsoup.parse(htmlBuilder.toString().trim()) - doc.body().childNodes().forEach { - convertHtmlNode(it)?.let { append(it) } - } + return htmlBuilder.toString().trim() } private fun convertHtmlNode(node: Node): ContentNode? { @@ -144,7 +147,7 @@ class JavadocParser( } } - private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { + private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { "link", "linkplain" -> { val valueElement = tag.referenceElement() val linkSignature = resolveLink(valueElement) @@ -164,6 +167,18 @@ class JavadocParser( 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 } @@ -193,4 +208,88 @@ class JavadocParser( } 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 + } + } -- cgit From a16ae6c02e1ec7270fb161c63908b5b3831bce8e Mon Sep 17 00:00:00 2001 From: Sean McQuillan Date: Thu, 26 Apr 2018 18:03:04 -0700 Subject: [backport] Support for {@inheritDoc} including grand inheritance and skip levels. Original: 0eda743 --- core/src/main/kotlin/Java/JavadocParser.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index f88f557d..554cd2d3 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -1,6 +1,8 @@ package org.jetbrains.dokka import com.intellij.psi.* +import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl +import com.intellij.psi.javadoc.* import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil @@ -41,14 +43,13 @@ class JavadocParser( paragraphs.forEach { result.append(it) } - val attrs = mutableListOf() - var since: DocumentationNode? = null docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) "deprecated" -> { - deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements(), element) + deprecatedContent = Content().apply { + convertJavadocElements(tag.contentElements(), element) + } } else -> { val subjectName = tag.getSubjectName() -- cgit From 74b228108445a8a9024b6892f6562d77c658fc64 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Thu, 3 May 2018 18:20:33 +0300 Subject: [backport] KT-24152: Support implicit sections inheritance Original: 5636fac --- core/src/main/kotlin/Java/JavadocParser.kt | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 554cd2d3..41b77aed 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -2,12 +2,12 @@ package org.jetbrains.dokka import com.intellij.psi.* import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl -import com.intellij.psi.javadoc.* import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.IncorrectOperationException 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 @@ -43,6 +43,17 @@ class JavadocParser( paragraphs.forEach { result.append(it) } + + 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()) + section.convertJavadocElements(tag.contentElements(), context) + } + } + } + docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) @@ -51,6 +62,7 @@ class JavadocParser( convertJavadocElements(tag.contentElements(), element) } } + in tagsToInherit -> {} else -> { val subjectName = tag.getSubjectName() val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) @@ -62,6 +74,32 @@ class JavadocParser( 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 } -- cgit From a50de81d3d0ce88d2fd8e91a55b203ba49e66eb1 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 4 May 2018 00:32:40 +0300 Subject: [backport] KT-24028: Add type info to param and return section in javadoc Original: a603157 --- core/src/main/kotlin/Java/JavadocParser.kt | 23 +++++++++++++++++++++++ core/src/main/kotlin/Model/Content.kt | 30 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 41b77aed..5e23e357 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -28,6 +28,18 @@ class JavadocParser( private val logger: DokkaLogger, private val signatureProvider: ElementSignatureProvider ) : JavaDocumentationParser { + + private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { + append(LazyContentBlock { + val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) + if (node != null) { + it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) + it.symbol(":") + it.text(" ") + } + }) + } + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment if (docComment == null) return JavadocParseResult.Empty @@ -49,6 +61,17 @@ class JavadocParser( 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 { it.kind == NodeKind.Parameter }?.detailOrNull(NodeKind.Type) + } + } + "return" -> { + section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } + } + } section.convertJavadocElements(tag.contentElements(), context) } } diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 1f5bbc83..010ee7ca 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -9,7 +9,7 @@ object ContentEmpty : ContentNode { } open class ContentBlock() : ContentNode { - val children = arrayListOf() + open val children = arrayListOf() fun append(node: ContentNode) { children.add(node) @@ -27,6 +27,34 @@ open class ContentBlock() : ContentNode { get() = children.sumBy { it.textLength } } +class NodeRenderContent( + val node: DocumentationNode, + val mode: LanguageService.RenderMode +): ContentNode { + override val textLength: Int + get() = 0 //TODO: Clarify? +} + +class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() { + private var computed = false + override val children: ArrayList + get() { + if (!computed) { + computed = true + fillChildren(this) + } + return super.children + } + + override fun equals(other: Any?): Boolean { + return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other) + } + + override fun hashCode(): Int { + return super.hashCode() + 31 * fillChildren.hashCode() + } +} + enum class IdentifierKind { TypeName, ParameterName, -- cgit From b00dabc4c53a71f745c29a135541b02f8dd7d266 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 4 May 2018 21:35:02 +0300 Subject: [backport] KT-24271: Support external link resolution in JavadocParser Original: ace3914 --- .../main/kotlin/Analysis/AnalysisEnvironment.kt | 8 ++-- .../kotlin/Java/JavaPsiDocumentationBuilder.kt | 10 ++++- core/src/main/kotlin/Java/JavadocParser.kt | 51 ++++++++++++++++------ .../kotlin/Kotlin/DescriptorDocumentationParser.kt | 12 +++-- .../Kotlin/ExternalDocumentationLinkResolver.kt | 14 ++++-- .../Kotlin/KotlinElementSignatureProvider.kt | 30 ++++++------- core/src/main/kotlin/Utilities/DokkaModules.kt | 3 +- 7 files changed, 87 insertions(+), 41 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt index 6854b323..d31fe187 100644 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt @@ -99,7 +99,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { } - fun createResolutionFacade(environment: KotlinCoreEnvironment): DokkaResolutionFacade { + fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair { val projectContext = ProjectContext(environment.project) val sourceFiles = environment.getSourceFiles() @@ -164,15 +164,17 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { builtIns = builtIns ) - resolverForProject.resolverForModule(library) // Required before module to initialize library properly + val resolverForLibrary = resolverForProject.resolverForModule(library) // Required before module to initialize library properly val resolverForModule = resolverForProject.resolverForModule(module) + val libraryModuleDescriptor = resolverForProject.descriptorForModule(library) val moduleDescriptor = resolverForProject.descriptorForModule(module) builtIns.initialize(moduleDescriptor, true) + val libraryResolutionFacade = DokkaResolutionFacade(environment.project, libraryModuleDescriptor, resolverForLibrary) val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule) val projectComponentManager = environment.project as MockComponentManager projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created)) - return created + return created to libraryResolutionFacade } fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) { diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 646096e5..8b30a6b4 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -45,10 +45,16 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger, signatureProvider: ElementSignatureProvider) { + @Inject constructor( + options: DocumentationOptions, + refGraph: NodeReferenceGraph, + logger: DokkaLogger, + signatureProvider: ElementSignatureProvider, + externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + ) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger, signatureProvider) + this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) } 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 5e23e357..e1b15cb2 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -26,7 +26,8 @@ interface JavaDocumentationParser { class JavadocParser( private val refGraph: NodeReferenceGraph, private val logger: DokkaLogger, - private val signatureProvider: ElementSignatureProvider + private val signatureProvider: ElementSignatureProvider, + private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver ) : JavaDocumentationParser { private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { @@ -197,25 +198,41 @@ class JavadocParser( private fun MutableContent.convertSeeTag(tag: PsiDocTag) { val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - val linkSignature = resolveLink(tag.referenceElement()) + + val valueElement = tag.referenceElement() + val externalLink = resolveExternalLink(valueElement) val text = ContentText(linkElement.text) - if (linkSignature != null) { - val linkNode = - ContentNodeLazyLink((tag.valueElement ?: linkElement).text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) - linkNode.append(text) - seeSection.append(linkNode) - } else { - seeSection.append(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 linkSignature = resolveLink(valueElement) - if (linkSignature != null) { + 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 link = "${labelText.htmlEscape()}" + 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 @@ -256,7 +273,15 @@ class JavadocParser( private fun PsiDocTag.linkElement(): PsiElement? = valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - private fun resolveLink(valueElement: PsiElement?): String? { + 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) diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt index 5f1118e8..7817da18 100644 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt @@ -34,8 +34,9 @@ class DescriptorDocumentationParser val resolutionFacade: DokkaResolutionFacade, val refGraph: NodeReferenceGraph, val sampleService: SampleProcessingService, - val signatureProvider: KotlinElementSignatureProvider - ) + val signatureProvider: KotlinElementSignatureProvider, + val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver +) { fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content = parseDocumentationAndDetails(descriptor, inline).first @@ -131,7 +132,12 @@ class DescriptorDocumentationParser 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).parseDocumentation(psi as PsiNamedElement) + val parseResult = JavadocParser( + refGraph, + logger, + signatureProvider, + externalDocumentationLinkResolver + ).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/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt index 7be37177..c766b869 100644 --- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt +++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt @@ -2,6 +2,7 @@ package org.jetbrains.dokka import com.google.inject.Inject import com.google.inject.Singleton +import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.util.io.* import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor @@ -10,10 +11,7 @@ import org.jetbrains.dokka.Utilities.ServiceLocator import org.jetbrains.dokka.Utilities.lookup import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor +import org.jetbrains.kotlin.load.java.descriptors.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe @@ -25,6 +23,7 @@ import java.net.URL import java.net.URLConnection import java.nio.file.Path import java.security.MessageDigest +import javax.inject.Named import kotlin.reflect.full.findAnnotation fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) } @@ -32,6 +31,7 @@ fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format( @Singleton class ExternalDocumentationLinkResolver @Inject constructor( val options: DocumentationOptions, + @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade, val logger: DokkaLogger ) { @@ -166,6 +166,12 @@ class ExternalDocumentationLinkResolver @Inject constructor( } } + fun buildExternalDocumentationLink(element: PsiElement): String? { + return element.extractDescriptor(libraryResolutionFacade)?.let { + buildExternalDocumentationLink(it) + } + } + fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? { val packageFqName: FqName = when (symbol) { diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt index 0a377dc1..bcac0182 100644 --- a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt +++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt @@ -2,7 +2,6 @@ package org.jetbrains.dokka import com.intellij.psi.PsiElement import com.intellij.psi.PsiMember -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.PsiPackage import org.jetbrains.kotlin.asJava.elements.KtLightElement import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -14,21 +13,22 @@ class KotlinElementSignatureProvider @Inject constructor( val resolutionFacade: DokkaResolutionFacade ) : ElementSignatureProvider { override fun signature(forPsi: PsiElement): String { - - val desc = when (forPsi) { - is KtLightElement<*, *> -> return signature(forPsi.kotlinOrigin!!) - is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName)) - is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) - else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] - } - - if (desc == null && (forPsi as? PsiNameIdentifierOwner)?.name == "remove") { - (forPsi as? PsiMember)?.getJavaOrKotlinMemberDescriptor(resolutionFacade) - } - - return desc?.let { signature(it) } + return forPsi.extractDescriptor(resolutionFacade) + ?.let { signature(it) } ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" } } override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() -} \ No newline at end of file +} + + +fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? { + val forPsi = this + + return when (forPsi) { + is KtLightElement<*, *> -> return (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade) + is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName)) + is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) + else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] + } +} diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt index 907d5ca7..732cbc48 100644 --- a/core/src/main/kotlin/Utilities/DokkaModules.kt +++ b/core/src/main/kotlin/Utilities/DokkaModules.kt @@ -24,8 +24,9 @@ class DokkaAnalysisModule(val environment: AnalysisEnvironment, val coreEnvironment = environment.createCoreEnvironment() binder.bind().toInstance(coreEnvironment) - val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) + val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment) binder.bind().toInstance(dokkaResolutionFacade) + binder.bind().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade) binder.bind().toInstance(options) -- cgit From 44ad68386658dde985207ea0aedb19c96d9b3f93 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 7 May 2018 16:37:12 +0300 Subject: [backport] KT-24277: Persist inpage anchors (aka bookmarks) from Javadoc Original: 4fddba5 --- core/src/main/kotlin/Java/JavadocParser.kt | 22 +++++++++++++--------- core/src/main/kotlin/Model/Content.kt | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index e1b15cb2..8cd274e7 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -183,15 +183,19 @@ class JavadocParser( } private fun createLink(element: Element): ContentBlock { - if (element.hasAttr("docref")) { - val docref = element.attr("docref") - return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) - } - return if (element.hasAttr("href")) { - val href = element.attr("href") - ContentExternalLink(href) - } else { - 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") + ContentExternalLink(href) + } + element.hasAttr("name") -> { + ContentBookmark(element.attr("name")) + } + else -> ContentBlock() } } diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 010ee7ca..4f142ca4 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -148,6 +148,8 @@ class ContentExternalLink(val href : String) : ContentBlock() { children.hashCode() * 31 + href.hashCode() } +data class ContentBookmark(val name: String): ContentBlock() + class ContentUnorderedList() : ContentBlock() class ContentOrderedList() : ContentBlock() class ContentListItem() : ContentBlock() -- cgit From 4301503416ed45783a81250295adf4b5a86c4280 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 7 May 2018 17:28:22 +0300 Subject: [backport] KT-24299: Fix local links resolved in incorrect context Original: fababd4 --- core/src/main/kotlin/Java/JavadocParser.kt | 14 +++++++++++++- core/src/main/kotlin/Model/Content.kt | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 8cd274e7..9f9ea017 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -12,6 +12,7 @@ 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 { @@ -190,7 +191,18 @@ class JavadocParser( } element.hasAttr("href") -> { val href = element.attr("href") - ContentExternalLink(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")) diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 4f142ca4..7c776bdb 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -149,6 +149,7 @@ class ContentExternalLink(val href : String) : ContentBlock() { } data class ContentBookmark(val name: String): ContentBlock() +data class ContentLocalLink(val href: String) : ContentBlock() class ContentUnorderedList() : ContentBlock() class ContentOrderedList() : ContentBlock() -- cgit From 7ef0cf32835d6d652e408a2115d58c37749320c5 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 29 Oct 2018 17:12:47 +0300 Subject: Cleanup support for NodeRenderContent --- core/src/main/kotlin/Formats/StructuredFormatService.kt | 4 ++++ core/src/main/kotlin/Java/JavadocParser.kt | 12 ++++++------ core/src/main/kotlin/Model/Content.kt | 4 ++-- core/src/test/kotlin/model/JavaTest.kt | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index ebc9cde6..410de281 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -152,6 +152,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + is NodeRenderContent -> { + val node = content.node + appendContent(languageService.render(node, content.mode)) + } is ContentNodeLink -> { val node = content.node val linkTo = if (node != null) locationHref(location, node) else "#" diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 9f9ea017..1144763b 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -33,12 +33,12 @@ class JavadocParser( private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { append(LazyContentBlock { - val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) - if (node != null) { - it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) - it.symbol(":") - it.text(" ") - } + val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList() + listOf(ContentBlock().apply { + append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) + symbol(":") + text(" ") + }) }) } diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 7c776bdb..87a8023a 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -35,13 +35,13 @@ class NodeRenderContent( get() = 0 //TODO: Clarify? } -class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() { +class LazyContentBlock(private val fillChildren: () -> List) : ContentBlock() { private var computed = false override val children: ArrayList get() { if (!computed) { computed = true - fillChildren(this) + children.addAll(fillChildren()) } return super.children } diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt index 66eb84f1..876d18c0 100644 --- a/core/src/test/kotlin/model/JavaTest.kt +++ b/core/src/test/kotlin/model/JavaTest.kt @@ -23,7 +23,7 @@ public class JavaTest { with(content.sections[1]) { assertEquals("Parameters", tag) assertEquals("value", subjectName) - assertEquals("render(Type:String,SUMMARY): is int parameter", toTestString()) + assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString()) } with(content.sections[2]) { assertEquals("Author", tag) -- cgit From 8afbc009483636adbc8562b0707439b595d59242 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 29 Oct 2018 17:13:42 +0300 Subject: Refactor code of javadoc paragraphs converting --- core/src/main/kotlin/Java/JavadocParser.kt | 39 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 1144763b..d6e46c50 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -1,11 +1,9 @@ package org.jetbrains.dokka import com.intellij.psi.* -import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil -import com.intellij.util.IncorrectOperationException import com.intellij.util.containers.isNullOrEmpty import org.jetbrains.kotlin.utils.keysToMap import org.jsoup.Jsoup @@ -43,20 +41,19 @@ class JavadocParser( } override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment - if (docComment == null) return JavadocParseResult.Empty + 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() - firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) - val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } - firstParagraph.children.removeAll(paragraphs) - if (!firstParagraph.isEmpty()) { + if (firstParagraphContents.isNotEmpty()) { + firstParagraphContents.forEach { firstParagraph.append(it) } result.append(firstParagraph) } - paragraphs.forEach { - result.append(it) - } + + result.appendAll(nodes.drop(firstParagraphContents.size)) if (element is PsiMethod) { val tagsByName = element.searchInheritedTags() @@ -67,14 +64,16 @@ class JavadocParser( when (tagName) { "param" -> { section.appendTypeElement(signature) { - it.details.find { it.kind == NodeKind.Parameter }?.detailOrNull(NodeKind.Type) + it.details + .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() } + ?.detailOrNull(NodeKind.Type) } } "return" -> { section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } } } - section.convertJavadocElements(tag.contentElements(), context) + section.appendAll(convertJavadocElements(tag.contentElements(), context)) } } } @@ -84,7 +83,7 @@ class JavadocParser( "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content().apply { - convertJavadocElements(tag.contentElements(), element) + appendAll(convertJavadocElements(tag.contentElements(), element)) } } in tagsToInherit -> {} @@ -92,7 +91,7 @@ class JavadocParser( val subjectName = tag.getSubjectName() val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - section.convertJavadocElements(tag.contentElements(), element) + section.appendAll(convertJavadocElements(tag.contentElements(), element)) } } } @@ -133,13 +132,17 @@ class JavadocParser( return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements } - private fun ContentBlock.convertJavadocElements(elements: Iterable, element: PsiNamedElement) { + private fun convertJavadocElements(elements: Iterable, element: PsiNamedElement): List { val doc = Jsoup.parse(expandAllForElements(elements, element)) - doc.body().childNodes().forEach { - convertHtmlNode(it)?.let { append(it) } + 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 { -- cgit From a2104d1e24a452cbfd2a76c204fc58475aa70057 Mon Sep 17 00:00:00 2001 From: Błażej Kardyś Date: Tue, 5 Feb 2019 16:05:15 +0100 Subject: Fix @deprecated visibility in JavaDoc (#420) #343 Fixed --- core/src/main/kotlin/Java/JavadocParser.kt | 7 ++++--- core/src/test/kotlin/javadoc/JavadocTest.kt | 10 ++++++++++ core/testdata/javadoc/deprecated.java | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 core/testdata/javadoc/deprecated.java (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index d6e46c50..39a4568a 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -83,7 +83,7 @@ class JavadocParser( "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content().apply { - appendAll(convertJavadocElements(tag.contentElements(), element)) + appendAll(convertJavadocElements(tag.contentElementsOrFirstChild(), element)) } } in tagsToInherit -> {} @@ -122,9 +122,10 @@ class JavadocParser( recursiveSearch(arrayOf(this)) return output.mapValues { it.value.values } } + private fun PsiDocTag.contentElementsOrFirstChild(): Collection = contentElements() + .takeIf { it.isNotEmpty() } ?: children.take(1) - - private fun PsiDocTag.contentElements(): Iterable { + private fun PsiDocTag.contentElements(): Collection { val tagValueElements = children .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } .dropWhile { it is PsiWhiteSpace } diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt index 9ca668d8..7f1b44da 100644 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -233,6 +233,16 @@ class JavadocTest { } } + @Test fun shouldHaveAllFunctionMarkedAsDeprecated() { + verifyJavadoc("testdata/javadoc/deprecated.java") { doc -> + val classDoc = doc.classNamed("bar.Banana")!! + + classDoc.methods().forEach { + assertNotNull((it as? DocumentationNodeAdapter)?.node?.deprecation) + } + } + } + private fun verifyJavadoc(name: String, withJdk: Boolean = false, withKotlinRuntime: Boolean = false, diff --git a/core/testdata/javadoc/deprecated.java b/core/testdata/javadoc/deprecated.java new file mode 100644 index 00000000..5a6cdd77 --- /dev/null +++ b/core/testdata/javadoc/deprecated.java @@ -0,0 +1,28 @@ +package bar; + +/** + * Just a fruit + */ +public class Banana { + private Double weight; + + /** + * Returns weight + * + * @return weight + * @deprecated + */ + public Double getWeight() { + return weight; + } + + /** + * Sets weight + * + * @param weight in grams + * @deprecated with message + */ + public void setWeight(Double weight) { + this.weight = weight; + } +} \ No newline at end of file -- cgit From ff25593e8af941c624f3bee8bc031ae53c3700d5 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 25 Feb 2019 19:45:09 +0300 Subject: Fix problem on consumer side, improve test (#420) --- core/src/main/kotlin/Java/JavadocParser.kt | 7 +++---- core/src/main/kotlin/javadoc/docbase.kt | 4 +--- core/src/test/kotlin/javadoc/JavadocTest.kt | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 39a4568a..d6e46c50 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -83,7 +83,7 @@ class JavadocParser( "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content().apply { - appendAll(convertJavadocElements(tag.contentElementsOrFirstChild(), element)) + appendAll(convertJavadocElements(tag.contentElements(), element)) } } in tagsToInherit -> {} @@ -122,10 +122,9 @@ class JavadocParser( recursiveSearch(arrayOf(this)) return output.mapValues { it.value.values } } - private fun PsiDocTag.contentElementsOrFirstChild(): Collection = contentElements() - .takeIf { it.isNotEmpty() } ?: children.take(1) - private fun PsiDocTag.contentElements(): Collection { + + private fun PsiDocTag.contentElements(): Iterable { val tagValueElements = children .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } .dropWhile { it is PsiWhiteSpace } diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt index fcc017fd..fbf8c464 100644 --- a/core/src/main/kotlin/javadoc/docbase.kt +++ b/core/src/main/kotlin/javadoc/docbase.kt @@ -75,9 +75,7 @@ open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node node.deprecation?.let { val content = it.content.asText() - if (content != null) { - result.add(TagImpl(this, "deprecated", content)) - } + result.add(TagImpl(this, "deprecated", content ?: "")) } return result.toTypedArray() diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt index 7f1b44da..04f8b920 100644 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -237,8 +237,8 @@ class JavadocTest { verifyJavadoc("testdata/javadoc/deprecated.java") { doc -> val classDoc = doc.classNamed("bar.Banana")!! - classDoc.methods().forEach { - assertNotNull((it as? DocumentationNodeAdapter)?.node?.deprecation) + classDoc.methods().forEach { method -> + assertTrue(method.tags().any { it.kind() == "deprecated" }) } } } -- cgit From f4aeac0a974837a18d0c18d42bd5980dffa043fb Mon Sep 17 00:00:00 2001 From: Dominik Schürmann Date: Mon, 21 Jan 2019 01:16:09 +0100 Subject: Preserve newlines in javadoc code blocks --- core/src/main/kotlin/Java/JavadocParser.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index d6e46c50..3411b5c7 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -160,13 +160,17 @@ class JavadocParser( return ContentText(node.text()) } else if (node is Element) { val childBlock = createBlock(node) - node.childNodes().forEach { - val child = convertHtmlNode(it) - if (child != null) { - childBlock.append(child) + if (childBlock is ContentBlockCode) { + childBlock.append(ContentText(node.text())) + } else { + node.childNodes().forEach { + val child = convertHtmlNode(it) + if (child != null) { + childBlock.append(child) + } } + return (childBlock) } - return (childBlock) } return null } -- cgit From 3fc4102597437dbf217c96b70000cac5fb9b591b Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 25 Feb 2019 21:34:14 +0300 Subject: Rework logic to avoid loosing tags inside pre --- core/src/main/kotlin/Java/JavadocParser.kt | 26 ++++++++++++-------------- core/testdata/format/javadocHtml.java | 12 ++++++++++++ core/testdata/format/javadocHtml.md | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) (limited to 'core/src/main/kotlin/Java/JavadocParser.kt') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 3411b5c7..70af73f9 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -155,32 +155,30 @@ class JavadocParser( return htmlBuilder.toString().trim() } - private fun convertHtmlNode(node: Node): ContentNode? { + private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? { if (node is TextNode) { - return ContentText(node.text()) + val text = if (insidePre) node.wholeText else node.text() + return ContentText(text) } else if (node is Element) { - val childBlock = createBlock(node) - if (childBlock is ContentBlockCode) { - childBlock.append(ContentText(node.text())) - } else { - node.childNodes().forEach { - val child = convertHtmlNode(it) - if (child != null) { - childBlock.append(child) - } + 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 childBlock } return null } - private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { + private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) { "p" -> ContentParagraph() "b", "strong" -> ContentStrong() "i", "em" -> ContentEmphasis() "s", "del" -> ContentStrikethrough() - "code" -> ContentCode() + "code" -> if (insidePre) ContentBlock() else ContentCode() "pre" -> ContentBlockCode() "ul" -> ContentUnorderedList() "ol" -> ContentOrderedList() diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java index 622116b2..9e77402e 100644 --- a/core/testdata/format/javadocHtml.java +++ b/core/testdata/format/javadocHtml.java @@ -9,6 +9,18 @@ * Code *
Block code
*
  • List Item
+ *
+ * with( some ) {
+ *    multi = lines
+ *    sample()
+ * }
+ * 
+ *
+ * {@code
+ *  with (some) {  }
+ * }
+ * 
+ * */ public class C { } diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md index 9b501fcb..77f6c829 100644 --- a/core/testdata/format/javadocHtml.md +++ b/core/testdata/format/javadocHtml.md @@ -16,6 +16,22 @@ Block code * List Item + +``` + + with( some ) { + multi = lines + sample() + } + ``` + + + +``` +with (some) { } + + ``` + ### Constructors | [<init>](-init-.md) | `C()`
**Bold** **Strong** *Italic* *Emphasized* | -- cgit