diff options
-rw-r--r-- | src/Java/JavaPsiDocumentationBuilder.kt | 170 | ||||
-rw-r--r-- | src/Java/JavadocParser.kt | 170 | ||||
-rw-r--r-- | src/Kotlin/DeclarationLinkResolver.kt | 44 | ||||
-rw-r--r-- | src/Kotlin/DocumentationBuilder.kt | 146 | ||||
-rw-r--r-- | src/Model/PackageDocs.kt | 6 | ||||
-rw-r--r-- | src/main.kt | 5 |
6 files changed, 278 insertions, 263 deletions
diff --git a/src/Java/JavaPsiDocumentationBuilder.kt b/src/Java/JavaPsiDocumentationBuilder.kt index bdc6a368..b1e3167d 100644 --- a/src/Java/JavaPsiDocumentationBuilder.kt +++ b/src/Java/JavaPsiDocumentationBuilder.kt @@ -1,177 +1,9 @@ package org.jetbrains.dokka import com.intellij.psi.* -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 org.jetbrains.dokka.DocumentationNode.Kind -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode - -data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { - companion object { - val Empty = JavadocParseResult(Content.Empty, null) - } -} - -interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): JavadocParseResult -} - -class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment - 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() }) - docComment.tags.forEach { tag -> - when(tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements()) - } - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.convertJavadocElements(tag.contentElements()) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private fun PsiDocTag.contentElements(): Iterable<PsiElement> { - val tagValueElements = children - .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } - .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } - return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements - } - - private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it)) - } else { - htmlBuilder.append(it.text) - } - } - val doc = Jsoup.parse(htmlBuilder.toString().trimStart()) - doc.body().childNodes().forEach { - convertHtmlNode(it) - } - } - - private fun ContentBlock.convertHtmlNode(node: Node) { - if (node is TextNode) { - append(ContentText(node.text())) - } else if (node is Element) { - val childBlock = createBlock(node) - node.childNodes().forEach { - childBlock.convertHtmlNode(it) - } - append(childBlock) - } - } - - private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - val docref = element.attr("docref") - if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) - } - val href = element.attr("href") - if (href != null) { - return ContentExternalLink(href) - } else { - return ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() - if (linkElement == null) { - 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.lookup(linkSignature)}) - linkNode.append(text) - seeSection.append(linkNode) - } else { - seeSection.append(text) - } - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.linkElement() - val linkSignature = resolveLink(valueElement) - if (linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>" - if (tag.name == "link") "<code>$link</code>" else link - } - else if (valueElement != null) { - valueElement.text - } else { - "" - } - } - "code", "literal" -> { - val text = StringBuilder() - tag.dataElements.forEach { text.append(it.text) } - val escaped = text.toString().trimStart().htmlEscape() - if (tag.name == "code") "<code>$escaped</code>" else escaped - } - else -> tag.text - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return getSignature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } -} - -private fun getSignature(element: PsiElement?) = when(element) { +fun getSignature(element: PsiElement?) = when(element) { is PsiClass -> element.qualifiedName is PsiField -> element.containingClass!!.qualifiedName + "#" + element.name is PsiMethod -> diff --git a/src/Java/JavadocParser.kt b/src/Java/JavadocParser.kt new file mode 100644 index 00000000..1378a5a7 --- /dev/null +++ b/src/Java/JavadocParser.kt @@ -0,0 +1,170 @@ +package org.jetbrains.dokka + +import com.intellij.psi.* +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 org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode + +data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { + companion object { + val Empty = JavadocParseResult(Content.Empty, null) + } +} + +interface JavaDocumentationParser { + fun parseDocumentation(element: PsiNamedElement): JavadocParseResult +} + +class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { + val docComment = (element as? PsiDocCommentOwner)?.docComment + 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() }) + docComment.tags.forEach { tag -> + when(tag.name) { + "see" -> result.convertSeeTag(tag) + "deprecated" -> { + deprecatedContent = Content() + deprecatedContent!!.convertJavadocElements(tag.contentElements()) + } + else -> { + val subjectName = tag.getSubjectName() + val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) + + section.convertJavadocElements(tag.contentElements()) + } + } + } + return JavadocParseResult(result, deprecatedContent) + } + + private fun PsiDocTag.contentElements(): Iterable<PsiElement> { + val tagValueElements = children + .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } + .dropWhile { it is PsiWhiteSpace } + .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } + return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements + } + + private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) { + val htmlBuilder = StringBuilder() + elements.forEach { + if (it is PsiInlineDocTag) { + htmlBuilder.append(convertInlineDocTag(it)) + } else { + htmlBuilder.append(it.text) + } + } + val doc = Jsoup.parse(htmlBuilder.toString().trimStart()) + doc.body().childNodes().forEach { + convertHtmlNode(it) + } + } + + private fun ContentBlock.convertHtmlNode(node: Node) { + if (node is TextNode) { + append(ContentText(node.text())) + } else if (node is Element) { + val childBlock = createBlock(node) + node.childNodes().forEach { + childBlock.convertHtmlNode(it) + } + append(childBlock) + } + } + + private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { + "p" -> ContentParagraph() + "b", "strong" -> ContentStrong() + "i", "em" -> ContentEmphasis() + "s", "del" -> ContentStrikethrough() + "code" -> ContentCode() + "pre" -> ContentBlockCode() + "ul" -> ContentUnorderedList() + "ol" -> ContentOrderedList() + "li" -> ContentListItem() + "a" -> createLink(element) + else -> ContentBlock() + } + + private fun createLink(element: Element): ContentBlock { + val docref = element.attr("docref") + if (docref != null) { + return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) + } + val href = element.attr("href") + if (href != null) { + return ContentExternalLink(href) + } else { + return ContentBlock() + } + } + + private fun MutableContent.convertSeeTag(tag: PsiDocTag) { + val linkElement = tag.linkElement() + if (linkElement == null) { + 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.lookup(linkSignature)}) + linkNode.append(text) + seeSection.append(linkNode) + } else { + seeSection.append(text) + } + } + + private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { + "link", "linkplain" -> { + val valueElement = tag.linkElement() + val linkSignature = resolveLink(valueElement) + if (linkSignature != null) { + val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text + val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>" + if (tag.name == "link") "<code>$link</code>" else link + } + else if (valueElement != null) { + valueElement.text + } else { + "" + } + } + "code", "literal" -> { + val text = StringBuilder() + tag.dataElements.forEach { text.append(it.text) } + val escaped = text.toString().trimStart().htmlEscape() + if (tag.name == "code") "<code>$escaped</code>" else escaped + } + else -> tag.text + } + + private fun PsiDocTag.linkElement(): PsiElement? = + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } + + private fun resolveLink(valueElement: PsiElement?): String? { + val target = valueElement?.reference?.resolve() + if (target != null) { + return getSignature(target) + } + return null + } + + fun PsiDocTag.getSubjectName(): String? { + if (name == "param" || name == "throws" || name == "exception") { + return valueElement?.text + } + return null + } +} diff --git a/src/Kotlin/DeclarationLinkResolver.kt b/src/Kotlin/DeclarationLinkResolver.kt new file mode 100644 index 00000000..cd536768 --- /dev/null +++ b/src/Kotlin/DeclarationLinkResolver.kt @@ -0,0 +1,44 @@ +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.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade + +class DeclarationLinkResolver + @Inject constructor(val resolutionFacade: ResolutionFacade, + val refGraph: NodeReferenceGraph, + val logger: DokkaLogger) { + fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock { + val symbol = try { + val symbols = resolveKDocLink(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) { + return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) }) + } + if ("/" in href) { + return ContentExternalLink(href) + } + logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") + return ContentExternalLink("#") + } + + fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { + if (symbols.isEmpty()) { + return null + } + val symbol = symbols.first() + if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + return symbol.overriddenDescriptors.firstOrNull() + } + return symbol + } + +}
\ No newline at end of file diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index 7dc4075b..c48f1495 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo import org.jetbrains.kotlin.idea.kdoc.KDocFinder -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection @@ -62,6 +61,7 @@ interface PackageDocumentationBuilder { class DocumentationBuilder(val resolutionFacade: ResolutionFacade, val session: ResolveSession, + val linkResolver: DeclarationLinkResolver, val options: DocumentationOptions, val refGraph: NodeReferenceGraph, val logger: DokkaLogger) : JavaDocumentationBuilder { @@ -98,7 +98,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, } val tree = parseMarkdown(kdocText) //println(tree.toTestString()) - val content = buildContent(tree, { href -> resolveContentLink(descriptor, href) }) + val content = buildContent(tree, { href -> linkResolver.resolveContentLink(descriptor, href) }) if (kdoc is KDocSection) { val tags = kdoc.getTags() tags.forEach { @@ -111,7 +111,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) val sectionContent = it.getContent() val markdownNode = parseMarkdown(sectionContent) - buildInlineContentTo(markdownNode, section, { href -> resolveContentLink(descriptor, href) }) + buildInlineContentTo(markdownNode, section, { href -> linkResolver.resolveContentLink(descriptor, href) }) } } } @@ -162,98 +162,13 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade, return Content.Empty to { node -> } } - fun DeclarationDescriptor.signature(): String = when(this) { - is ClassDescriptor, is PackageFragmentDescriptor -> DescriptorUtils.getFqName(this).asString() - is PropertyDescriptor -> containingDeclaration.signature() + "#" + name + receiverSignature() - is FunctionDescriptor -> containingDeclaration.signature() + "#" + name + parameterSignature() - is ValueParameterDescriptor -> containingDeclaration.signature() + ":" + name - is TypeParameterDescriptor -> containingDeclaration.signature() + "<" + name - - else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this") - } - - fun PropertyDescriptor.receiverSignature(): String { - val receiver = extensionReceiverParameter - if (receiver != null) { - return "#" + receiver.type.signature() - } - return "" - } - - fun CallableMemberDescriptor.parameterSignature(): String { - val params = valueParameters.map { it.type }.toArrayList() - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - params.add(0, extensionReceiver.type) - } - return "(" + params.map { it.signature() }.joinToString() + ")" - } - - fun KtType.signature(): String { - val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>" - val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() - if (typeName == "Array" && arguments.size == 1) { - return "Array<" + arguments.first().type.signature() + ">" - } - return typeName - } - - fun DeclarationDescriptor.sourceLocation(): String? { - if (this is DeclarationDescriptorWithSource) { - val psi = (this.source as? PsiSourceElement)?.getPsi() - if (psi != null) { - val fileName = psi.containingFile.name - val lineNumber = psi.lineNumber() - return if (lineNumber != null) "$fileName:$lineNumber" else fileName - } - } - return null - } - - fun DeclarationDescriptor.signatureWithSourceLocation(): String { - val signature = signature() - val sourceLocation = sourceLocation() - return if (sourceLocation != null) "$signature ($sourceLocation)" else signature - } - - fun resolveContentLink(descriptor: DeclarationDescriptor, href: String): ContentBlock { - val symbol = try { - val symbols = resolveKDocLink(resolutionFacade, descriptor, 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) { - return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) }) - } - if ("/" in href) { - return ContentExternalLink(href) - } - logger.warn("Unresolved link to $href in doc comment of ${descriptor.signatureWithSourceLocation()}") - return ContentExternalLink("#") - } - - fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { - if (symbols.isEmpty()) { - return null - } - val symbol = symbols.first() - if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - return symbol.overriddenDescriptors.firstOrNull() - } - return symbol - } - fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf() private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) { val subjectName = seeTag.getSubjectName() if (subjectName != null) { val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null) - val link = resolveContentLink(descriptor, subjectName) + val link = linkResolver.resolveContentLink(descriptor, subjectName) link.append(ContentText(subjectName)) val para = ContentParagraph() para.append(link) @@ -782,3 +697,56 @@ fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor return null } +fun DeclarationDescriptor.signature(): String = when(this) { + is ClassDescriptor, is PackageFragmentDescriptor -> DescriptorUtils.getFqName(this).asString() + is PropertyDescriptor -> containingDeclaration.signature() + "#" + name + receiverSignature() + is FunctionDescriptor -> containingDeclaration.signature() + "#" + name + parameterSignature() + is ValueParameterDescriptor -> containingDeclaration.signature() + ":" + name + is TypeParameterDescriptor -> containingDeclaration.signature() + "<" + name + + else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this") +} + +fun PropertyDescriptor.receiverSignature(): String { + val receiver = extensionReceiverParameter + if (receiver != null) { + return "#" + receiver.type.signature() + } + return "" +} + +fun CallableMemberDescriptor.parameterSignature(): String { + val params = valueParameters.map { it.type }.toArrayList() + val extensionReceiver = extensionReceiverParameter + if (extensionReceiver != null) { + params.add(0, extensionReceiver.type) + } + return "(" + params.map { it.signature() }.joinToString() + ")" +} + +fun KtType.signature(): String { + val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>" + val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() + if (typeName == "Array" && arguments.size == 1) { + return "Array<" + arguments.first().type.signature() + ">" + } + return typeName +} + +fun DeclarationDescriptor.signatureWithSourceLocation(): String { + val signature = signature() + val sourceLocation = sourceLocation() + return if (sourceLocation != null) "$signature ($sourceLocation)" else signature +} + +fun DeclarationDescriptor.sourceLocation(): String? { + if (this is DeclarationDescriptorWithSource) { + val psi = (this.source as? PsiSourceElement)?.getPsi() + if (psi != null) { + val fileName = psi.containingFile.name + val lineNumber = psi.lineNumber() + return if (lineNumber != null) "$fileName:$lineNumber" else fileName + } + } + return null +} diff --git a/src/Model/PackageDocs.kt b/src/Model/PackageDocs.kt index 99da327b..a1cfddf9 100644 --- a/src/Model/PackageDocs.kt +++ b/src/Model/PackageDocs.kt @@ -5,7 +5,7 @@ import org.intellij.markdown.MarkdownTokenTypes import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import java.io.File -public class PackageDocs(val documentationBuilder: DocumentationBuilder?, +public class PackageDocs(val linkResolver: DeclarationLinkResolver?, val linkResolveContext: DeclarationDescriptor?, val logger: DokkaLogger) { public val moduleContent: MutableContent = MutableContent() @@ -48,8 +48,8 @@ public class PackageDocs(val documentationBuilder: DocumentationBuilder?, _packageContent.getOrPut(packageName) { -> MutableContent() } private fun resolveContentLink(href: String): ContentBlock { - if (linkResolveContext != null && documentationBuilder != null) { - return documentationBuilder.resolveContentLink(linkResolveContext, href) + if (linkResolveContext != null && linkResolver != null) { + return linkResolver.resolveContentLink(linkResolveContext, href) } return ContentExternalLink("#") } diff --git a/src/main.kt b/src/main.kt index 46b9a2dc..1bb0d643 100644 --- a/src/main.kt +++ b/src/main.kt @@ -216,8 +216,9 @@ fun buildDocumentationModule(environment: AnalysisEnvironment, val fragments = fragmentFiles.map { session.getPackageFragment(it.packageFqName) }.filterNotNull().distinct() val refGraph = NodeReferenceGraph() - val documentationBuilder = DocumentationBuilder(resolutionFacade, session, options, refGraph, logger) - val packageDocs = PackageDocs(documentationBuilder, fragments.firstOrNull(), logger) + val linkResolver = DeclarationLinkResolver(resolutionFacade, refGraph, logger) + val documentationBuilder = DocumentationBuilder(resolutionFacade, session, linkResolver, options, refGraph, logger) + val packageDocs = PackageDocs(linkResolver, fragments.firstOrNull(), logger) for (include in includes) { packageDocs.parse(include) } |