package org.jetbrains.dokka import com.google.inject.Inject 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 class DeclarationLinkResolver @Inject constructor(val resolutionFacade: DokkaResolutionFacade, val refGraph: NodeReferenceGraph, val logger: DokkaLogger, val passConfiguration: DokkaConfiguration.PassConfiguration, val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, val elementSignatureProvider: ElementSignatureProvider) { fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { val symbol = try { val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, resolutionFacade, fromDescriptor, null, href.split('.').toList()) findTargetSymbol(symbols) } catch(e: Exception) { null } // don't include unresolved links in generated doc // assume that if an href doesn't contain '/', it's not an attempt to reference an external file if (symbol != null) { val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol) if (externalHref != null) { return ContentExternalLink(externalHref) } val signature = elementSignatureProvider.signature(symbol) val referencedAt = fromDescriptor.signatureWithSourceLocation() return ContentNodeLazyLink(href, { -> val target = refGraph.lookup(signature) if (target == null) { logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt") } target }) } if ("/" in href) { return ContentExternalLink(href) } return null } fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) = tryResolveContentLink(fromDescriptor, href) ?: run { logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") ContentExternalLink("#") } fun findTargetSymbol(symbols: Collection): DeclarationDescriptor? { if (symbols.isEmpty()) { return null } val symbol = symbols.first() if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { return symbol.overriddenDescriptors.firstOrNull() } if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) { return symbol.classDescriptor } return symbol } }