From 76e4d3cbc7cbbe1d35fb2e0c1ba59d3c86e0daf2 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Mon, 23 Mar 2015 18:52:05 +0100 Subject: parse included Markdown files to retrieve documentation for modules and packages --- src/Formats/StructuredFormatService.kt | 28 +++++++++++------ src/Java/JavaDocumentationBuilder.kt | 2 +- src/Kotlin/ContentBuilder.kt | 6 ++++ src/Kotlin/DocumentationBuilder.kt | 5 +-- src/Model/Content.kt | 2 ++ src/Model/DocumentationNode.kt | 6 ++-- src/Model/PackageDocs.kt | 56 ++++++++++++++++++++++++++++++++++ src/main.kt | 38 +++++++++++------------ 8 files changed, 107 insertions(+), 36 deletions(-) create mode 100644 src/Model/PackageDocs.kt diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index 233dd9e3..0ee3c888 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -84,12 +84,9 @@ public abstract class StructuredFormatService(locationService: LocationService, append(formatLink(linkText, content.href)) } } - is ContentParagraph -> { - appendParagraph(this, formatText(location, content.children)) - } - is ContentBlockCode -> { - appendBlockCode(this, formatText(location, content.children), content.language) - } + is ContentParagraph -> appendParagraph(this, formatText(location, content.children)) + is ContentBlockCode -> appendBlockCode(this, formatText(location, content.children), content.language) + is ContentHeading -> appendHeader(this, formatText(location, content.children), content.level) is ContentBlock -> append(formatText(location, content.children)) } }.toString() @@ -133,6 +130,9 @@ public abstract class StructuredFormatService(locationService: LocationService, } } + private fun DocumentationNode.isModuleOrPackage(): Boolean = + kind == DocumentationNode.Kind.Module || kind == DocumentationNode.Kind.Package + protected open fun appendAsSignature(to: StringBuilder, block: () -> Unit) { block() } @@ -206,10 +206,18 @@ public abstract class StructuredFormatService(locationService: LocationService, } fun appendLocation(location: Location, to: StringBuilder, nodes: Iterable) { - val breakdownByName = nodes.groupBy { node -> node.name } - for ((name, items) in breakdownByName) { - appendHeader(to, formatText(name)) - appendDocumentation(location, to, items) + val singleNode = nodes.singleOrNull() + if (singleNode != null && singleNode.isModuleOrPackage()) { + if (singleNode.kind == DocumentationNode.Kind.Package) { + appendHeader(to, "Package " + formatText(singleNode.name), 2) + } + to.append(formatText(location, singleNode.content)) + } else { + val breakdownByName = nodes.groupBy { node -> node.name } + for ((name, items) in breakdownByName) { + appendHeader(to, formatText(name)) + appendDocumentation(location, to, items) + } } } diff --git a/src/Java/JavaDocumentationBuilder.kt b/src/Java/JavaDocumentationBuilder.kt index 6624037c..d57210ae 100644 --- a/src/Java/JavaDocumentationBuilder.kt +++ b/src/Java/JavaDocumentationBuilder.kt @@ -14,7 +14,7 @@ public class JavaDocumentationBuilder(private val options: DocumentationOptions, if (file.getClasses().all { skipElement(it) }) { return } - val packageNode = module.findOrCreatePackageNode(file.getPackageName()) + val packageNode = module.findOrCreatePackageNode(file.getPackageName(), emptyMap()) packageNode.appendChildren(file.getClasses()) { build() } } diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt index 742ac58b..f454c8fe 100644 --- a/src/Kotlin/ContentBuilder.kt +++ b/src/Kotlin/ContentBuilder.kt @@ -31,6 +31,12 @@ public fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver } when (node.type) { + MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1)) + MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2)) + MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3)) + MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4)) + MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5)) + MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6)) MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList()) MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList()) MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem()) diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index e193817a..8910c3e3 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -389,7 +389,8 @@ class DocumentationBuilder(val session: ResolveSession, return this } - fun DocumentationNode.appendFragments(fragments: Collection) { + fun DocumentationModule.appendFragments(fragments: Collection, + packageContent: Map) { val descriptors = hashMapOf>() for ((name, parts) in fragments.groupBy { it.fqName }) { descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() }) @@ -397,7 +398,7 @@ class DocumentationBuilder(val session: ResolveSession, for ((packageName, declarations) in descriptors) { if (options.skipEmptyPackages && declarations.none { it.isDocumented()}) continue logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(packageName) + val packageNode = findOrCreatePackageNode(packageName, packageContent) val externalClassNodes = hashMapOf() declarations.forEach { descriptor -> if (descriptor.isDocumented()) { diff --git a/src/Model/Content.kt b/src/Model/Content.kt index 0244359e..30ec1fda 100644 --- a/src/Model/Content.kt +++ b/src/Model/Content.kt @@ -81,6 +81,8 @@ public class ContentUnorderedList() : ContentBlock() public class ContentOrderedList() : ContentBlock() public class ContentListItem() : ContentBlock() +public class ContentHeading(val level: Int) : ContentBlock() + public class ContentSection(public val tag: String, public val subjectName: String?) : ContentBlock() { override fun equals(other: Any?): Boolean = super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index 9e8a981f..6800abef 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -119,12 +119,14 @@ val DocumentationNode.path: List return parent.path + this } -fun DocumentationNode.findOrCreatePackageNode(packageName: String): DocumentationNode { +fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map): DocumentationNode { val existingNode = members(DocumentationNode.Kind.Package).firstOrNull { it.name == packageName } if (existingNode != null) { return existingNode } - val newNode = DocumentationNode(packageName, Content.Empty, DocumentationNode.Kind.Package) + val newNode = DocumentationNode(packageName, + packageContent.getOrElse(packageName) { Content.Empty }, + DocumentationNode.Kind.Package) append(newNode, DocumentationReference.Kind.Member) return newNode } diff --git a/src/Model/PackageDocs.kt b/src/Model/PackageDocs.kt new file mode 100644 index 00000000..3b0be547 --- /dev/null +++ b/src/Model/PackageDocs.kt @@ -0,0 +1,56 @@ +package org.jetbrains.dokka + +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import java.io.File + +public class PackageDocs(val documentationBuilder: DocumentationBuilder, + val linkResolveContext: DeclarationDescriptor?, + val logger: DokkaLogger) { + public val moduleContent: MutableContent = MutableContent() + private val _packageContent: MutableMap = hashMapOf() + public val packageContent: Map + get() = _packageContent + + fun parse(file: String) { + val file = File(file) + if (file.exists()) { + val text = file.readText() + val tree = parseMarkdown(text) + var targetContent: MutableContent = moduleContent + tree.children.forEach { + if (it.type == MarkdownElementTypes.ATX_1) { + val headingText = it.child(MarkdownTokenTypes.TEXT)?.text + if (headingText != null) { + targetContent = findTargetContent(headingText) + } + } else { + buildContentTo(it, targetContent, { resolveContentLink(it) }) + } + } + } else { + logger.warn("Include file $file was not found.") + } + } + + private fun findTargetContent(heading: String): MutableContent { + if (heading.startsWith("Module") || heading.startsWith("module")) { + return moduleContent + } + if (heading.startsWith("Package") || heading.startsWith("package")) { + return findOrCreatePackageContent(heading.substring("package".length()).trim()) + } + return findOrCreatePackageContent(heading) + } + + private fun findOrCreatePackageContent(packageName: String) = + _packageContent.getOrPut(packageName) { -> MutableContent() } + + private fun resolveContentLink(href: String): ContentBlock { + if (linkResolveContext != null) { + return documentationBuilder.resolveContentLink(linkResolveContext, href) + } + return ContentExternalLink("#") + } +} \ No newline at end of file diff --git a/src/main.kt b/src/main.kt index 8c7e936d..aa3da881 100644 --- a/src/main.kt +++ b/src/main.kt @@ -1,17 +1,21 @@ package org.jetbrains.dokka -import com.sampullara.cli.* -import com.intellij.openapi.util.* -import org.jetbrains.kotlin.cli.common.messages.* -import org.jetbrains.kotlin.cli.common.arguments.* -import org.jetbrains.kotlin.utils.PathUtil -import java.io.File +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment import com.intellij.psi.PsiJavaFile -import org.jetbrains.kotlin.config.CommonConfigurationKeys import com.intellij.psi.PsiManager -import com.intellij.openapi.vfs.VirtualFileManager +import com.sampullara.cli.Args +import com.sampullara.cli.Argument +import org.jetbrains.kotlin.cli.common.arguments.ValueDescription +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File class DokkaArguments { Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)") @@ -215,24 +219,16 @@ fun buildDocumentationModule(environment: AnalysisEnvironment, val fragmentFiles = environment.getSourceFiles().filter(filesToDocumentFilter) val fragments = fragmentFiles.map { session.getPackageFragment(it.getPackageFqName()) }.filterNotNull().distinct() - val moduleContent = Content() val refGraph = NodeReferenceGraph() val documentationBuilder = DocumentationBuilder(session, options, refGraph, logger) + val packageDocs = PackageDocs(documentationBuilder, fragments.firstOrNull(), logger) for (include in includes) { - val file = File(include) - if (file.exists()) { - val text = file.readText() - val tree = parseMarkdown(text) - val content = buildContent(tree, {href -> documentationBuilder.resolveContentLink(fragments.first(), href)}) - moduleContent.children.addAll(content.children) - } else { - logger.warn("Include file $file was not found.") - } + packageDocs.parse(include) } - val documentationModule = DocumentationModule(moduleName, moduleContent) + val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent) with(documentationBuilder) { - documentationModule.appendFragments(fragments) + documentationModule.appendFragments(fragments, packageDocs.packageContent) } val javaFiles = environment.getJavaSourceFiles().filter(filesToDocumentFilter) -- cgit