From bfd9ffd13ed6b6916790f5f0de5f9523db71b22e Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 30 Jan 2015 17:59:15 +0100 Subject: load sections from KDoc PSI, not through Markdown extensions --- lib/markdown.jar | Bin 387482 -> 200833 bytes src/Analysis/CommentsAPI.kt | 41 ---------------- src/Formats/StructuredFormatService.kt | 40 +++++++++------ src/Kotlin/ContentBuilder.kt | 48 +++++++----------- src/Kotlin/DocumentationBuilder.kt | 29 ++++++++++- src/Markdown/MarkdownProcessor.kt | 6 +-- src/Model/Content.kt | 71 ++++++++++----------------- src/Model/DocumentationNode.kt | 6 +-- src/main.kt | 2 +- test/data/comments/directive.kt | 8 +-- test/data/comments/emptySection.kt | 2 +- test/data/comments/explicitSummary.kt | 6 --- test/data/comments/multilineSection.kt | 2 +- test/data/comments/section1.kt | 2 +- test/data/comments/section2.kt | 4 +- test/data/comments/sectionWithBracedLabel.kt | 5 -- test/data/format/paramTag.kt | 6 +++ test/data/format/paramTag.md | 20 ++++++++ test/data/format/throwsTag.kt | 5 ++ test/data/format/throwsTag.md | 19 +++++++ test/src/format/MarkdownFormatTest.kt | 12 +++++ test/src/model/CommentTest.kt | 45 ++++------------- 22 files changed, 184 insertions(+), 195 deletions(-) delete mode 100644 src/Analysis/CommentsAPI.kt delete mode 100644 test/data/comments/explicitSummary.kt delete mode 100644 test/data/comments/sectionWithBracedLabel.kt create mode 100644 test/data/format/paramTag.kt create mode 100644 test/data/format/paramTag.md create mode 100644 test/data/format/throwsTag.kt create mode 100644 test/data/format/throwsTag.md diff --git a/lib/markdown.jar b/lib/markdown.jar index 9ab8c95c..681bcd43 100644 Binary files a/lib/markdown.jar and b/lib/markdown.jar differ diff --git a/src/Analysis/CommentsAPI.kt b/src/Analysis/CommentsAPI.kt deleted file mode 100644 index ce453113..00000000 --- a/src/Analysis/CommentsAPI.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.kdoc.psi.api.* - -/** - * Retrieves PSI elements representing documentation from the [DeclarationDescriptor] - * - * $$receiver: [DeclarationDescriptor] to get documentation nodes from - */ -fun DeclarationDescriptor.getDocumentationElements(): List { - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(this) - if (psiElement == null) - return listOf() - - return psiElement.children() // visit children - .takeWhile { it is KDoc } // all KDoc - .map { it as KDoc } // cast - .toList() - .reverse() // make reversed list -} - -/** - * Extracts text from KDoc, removes comment symbols and trims whitespace - */ -fun KDoc?.extractText(): String { - if (this == null) - return "" - val text = getText() - if (text == null) - return "" - val lines = text.replace("\r", "").split("\n") - return lines.map { - val comment = it.trim().dropWhile { it == '/' || it == '*' } - (if (comment.endsWith("*/")) - comment.substring(0, comment.length() - 2) - else - comment).trim() - }.join("\n").trim("\n") -} \ No newline at end of file diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index d2fbdc10..fe127732 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -81,36 +81,48 @@ public abstract class StructuredFormatService(val locationService: LocationServi } fun appendDescription(location: Location, to: StringBuilder, nodes: Iterable) { - val described = nodes.filter { it.hasDescription() } + val described = nodes.filter { it.hasDescriptionOrTags() } if (described.any()) { val single = described.size() == 1 - appendHeader(to, "Description", 3) + if (described.any { it.content.description != ContentEmpty }) { + appendHeader(to, "Description", 3) + } for (node in described) { if (!single) { appendBlockCode(to, formatText(location, languageService.render(node))) } appendLine(to, formatText(location, node.content.description)) appendLine(to) - for ((label, section) in node.content.sections) { - if (!isDescriptionSection(label, node)) continue - appendLine(to, formatStrong(formatText(label))) + + node.content.getSectionsWithSubjects().forEach { + appendSectionWithSubject(it.getKey(), location, it.getValue(), to) + } + + for (section in node.content.sections) { + if (section.subjectName != null) continue + appendLine(to, formatStrong(formatText(section.label))) appendLine(to, formatText(location, section)) } } } } - private fun DocumentationNode.hasDescription() = - content.description != ContentEmpty || content.sections.any { isDescriptionSection(it.key, this) } - - private fun isDescriptionSection(label: String, node: DocumentationNode): Boolean { - if (label.startsWith("$")) - return false - if (node.members.any { it.name == label }) - return false - return true + fun appendSectionWithSubject(title: String, location: Location, parameterSections: List, to: StringBuilder) { + appendHeader(to, title, 3) + parameterSections.forEach { + val parameterName = it.subjectName + if (parameterName != null) { + to.append(formatCode(parameterName)).append(" - ") + val formatted = formatText(location, it) + to.append(formatted) + appendLine(to) + } + } } + private fun DocumentationNode.hasDescriptionOrTags() = + content.description != ContentEmpty || !content.sections.isEmpty() + fun appendSummary(location: Location, to: StringBuilder, nodes: Iterable) { val breakdownBySummary = nodes.groupByTo(LinkedHashMap()) { node -> formatText(location, node.summary) diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt index c943a505..95e08c92 100644 --- a/src/Kotlin/ContentBuilder.kt +++ b/src/Kotlin/ContentBuilder.kt @@ -9,10 +9,16 @@ import org.intellij.markdown.* import org.jetbrains.kotlin.psi.JetDeclarationWithBody import org.jetbrains.kotlin.psi.JetBlockExpression -public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: DeclarationDescriptor): Content { +public fun DocumentationBuilder.buildContent(tree: MarkdownNode): Content { + val result = Content() + buildContentTo(tree, result) + return result +} + +public fun DocumentationBuilder.buildContentTo(tree: MarkdownNode, target: ContentNode) { // println(tree.toTestString()) val nodeStack = ArrayDeque() - nodeStack.push(Content()) + nodeStack.push(target) tree.visit {(node, processChildren) -> val parent = nodeStack.peek()!! @@ -47,22 +53,6 @@ public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: Dec processChildren() parent.append(nodeStack.pop()) } - MarkdownTokenTypes.SECTION_ID -> { - if (parent !is ContentSection) { - val text = node.text.trimLeading("$").trim("{", "}") - val name = text.substringBefore(" ") - val params = text.substringAfter(" ") - when (name) { - "code" -> parent.append(functionBody(descriptor, params)) - } - } - } - MarkdownElementTypes.SECTION -> { - val label = node.child(MarkdownTokenTypes.SECTION_ID)?.let { it.text.trimLeading("$").trim("{", "}") } ?: "" - nodeStack.push(ContentSection(label)) - processChildren() - parent.append(nodeStack.pop()) - } MarkdownElementTypes.INLINE_LINK -> { val label = node.child(MarkdownElementTypes.LINK_TEXT)?.child(MarkdownTokenTypes.TEXT) val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) @@ -88,7 +78,7 @@ public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: Dec } MarkdownTokenTypes.WHITE_SPACE, MarkdownTokenTypes.EOL -> { - if (nodeStack.peek() is ContentParagraph && node.parent?.children?.last() != node) { + if (keepWhitespace(nodeStack.peek()) && node.parent?.children?.last() != node) { nodeStack.push(ContentText(node.text)) processChildren() parent.append(nodeStack.pop()) @@ -105,10 +95,7 @@ public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: Dec parent.append(nodeStack.pop()) } MarkdownTokenTypes.COLON -> { - // TODO fix markdown parser - if (!isColonAfterSectionLabel(node)) { - parent.append(ContentText(node.text)) - } + parent.append(ContentText(node.text)) } MarkdownTokenTypes.DOUBLE_QUOTE, MarkdownTokenTypes.LT, @@ -120,9 +107,16 @@ public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: Dec } } } - return nodeStack.pop() as Content } +private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection + +public fun DocumentationBuilder.buildInlineContentTo(tree: MarkdownNode, target: ContentNode) { + val inlineContent = tree.children.firstOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) + inlineContent.forEach { + buildContentTo(it, target) + } +} fun DocumentationBuilder.functionBody(descriptor: DeclarationDescriptor, functionName: String): ContentNode { val scope = getResolutionScope(descriptor) @@ -176,9 +170,3 @@ private fun DocumentationBuilder.resolveInScope(functionName: String, scope: Jet return symbol } - -private fun isColonAfterSectionLabel(node: MarkdownNode): Boolean { - val parent = node.parent - return parent != null && parent.type == MarkdownElementTypes.SECTION && parent.children.size() >= 2 && - node == parent.children[1]; -} diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index be6d5d7c..053615bc 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -18,6 +18,10 @@ import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.PsiElement import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.psi.JetParameter +import org.jetbrains.kotlin.kdoc.findKDoc +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag public data class DocumentationOptions(val includeNonPublic: Boolean = false, val sourceLinks: List) @@ -36,13 +40,34 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati val packages = hashMapOf() fun parseDocumentation(descriptor: DeclarationDescriptor): Content { - val docText = descriptor.getDocumentationElements().map { it.extractText() }.join("\n") + val kdoc = findKDoc(descriptor) + val docText = kdoc?.getContent() ?: "" val tree = parseMarkdown(docText) //println(tree.toTestString()) - val content = buildContent(tree, descriptor) + val content = buildContent(tree) + if (kdoc is KDocSection) { + val tags = PsiTreeUtil.getChildrenOfType(kdoc, javaClass()) + tags?.forEach { + if (it.getName() == "code") { + content.append(functionBody(descriptor, it.getContent())) + } else { + val section = content.addSection(displayName(it.getName()), it.getSubjectName()) + val sectionContent = it.getContent() + val markdownNode = parseMarkdown(sectionContent) + buildInlineContentTo(markdownNode, section) + } + } + } return content } + fun displayName(sectionName: String?): String? = + when(sectionName) { + "param" -> "Parameters" + "throws", "exception" -> "Exceptions" + else -> sectionName + } + fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) { links.put(node, descriptor) } diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt index 05c4a7ec..7e601905 100644 --- a/src/Markdown/MarkdownProcessor.kt +++ b/src/Markdown/MarkdownProcessor.kt @@ -3,7 +3,7 @@ package org.jetbrains.dokka import org.intellij.markdown.* import org.intellij.markdown.ast.* import org.intellij.markdown.parser.* -import org.intellij.markdown.parser.dialects.KDocMarkerProcessor +import org.intellij.markdown.parser.dialects.commonmark.CommonMarkMarkerProcessor class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { val children: List = node.children.map { MarkdownNode(it, this, markdown) } @@ -134,12 +134,12 @@ public fun MarkdownNode.toHtml(): String { fun parseMarkdown(markdown: String): MarkdownNode { if (markdown.isEmpty()) return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown) - return MarkdownNode(MarkdownParser(KDocMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown), null, markdown) + return MarkdownNode(MarkdownParser(CommonMarkMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown), null, markdown) } fun markdownToHtml(markdown: String): String { - val tree = MarkdownParser(KDocMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown) + val tree = MarkdownParser(CommonMarkMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown) val markdownTree = MarkdownNode(tree, null, markdown) val ast = markdownTree.toTestString() return markdownTree.toHtml() diff --git a/src/Model/Content.kt b/src/Model/Content.kt index 8491fd88..2fae317d 100644 --- a/src/Model/Content.kt +++ b/src/Model/Content.kt @@ -1,7 +1,5 @@ package org.jetbrains.dokka -import kotlin.properties.Delegates - public abstract class ContentNode { val children = arrayListOf() @@ -34,7 +32,7 @@ public class ContentNodeLink(val node : DocumentationNode) : ContentBlock() public class ContentExternalLink(val href : String) : ContentBlock() public class ContentList() : ContentBlock() public class ContentListItem() : ContentBlock() -public class ContentSection(public val label: String) : ContentBlock() +public class ContentSection(public val label: String, public val subjectName: String?) : ContentBlock() fun content(body: ContentNode.() -> Unit): ContentNode { val block = ContentBlock() @@ -54,53 +52,38 @@ fun ContentNode.link(to: DocumentationNode, body: ContentNode.() -> Unit) { } public class Content() : ContentNode() { - public val sections: Map by Delegates.lazy { - val map = linkedMapOf() - for (child in children) { - if (child is ContentSection) - map.put(child.label, child) - } - - if ("\$summary" !in map && "\$description" !in map) { - // no explicit summary and description, convert anonymous section - val anonymous = map[""] - if (anonymous != null) { - map.remove("") - val summary = ContentSection("\$summary") - val description = ContentSection("\$description") - - val summaryNodes = anonymous.children.take(1) - val descriptionNodes = anonymous.children.drop(1) - - if (summaryNodes.any()) { - summary.children.addAll(summaryNodes) - map.put("\$summary", summary) - } - - if (descriptionNodes.any()) { - description.children.addAll(descriptionNodes) - map.put("\$description", description) - } - } - } - map + private val sectionList = arrayListOf() + public val sections: List + get() = sectionList + + fun addSection(name: String?, subjectName: String?): ContentSection { + val section = ContentSection(name ?: "", subjectName) + sectionList.add(section) + return section } - public val summary: ContentNode get() { - return sections["\$summary"] ?: ContentNode.empty + fun findSectionByName(name: String): ContentSection? = + sections.firstOrNull { it.label == name } + + fun getSectionsWithSubjects(): Map> = + sections.filter { it.subjectName != null }.groupBy { it.label } + + public val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty + + public val description: ContentNode get() { + val descriptionNodes = children.drop(1) + if (descriptionNodes.isEmpty()) { + return ContentEmpty + } + val result = ContentSection("\$description", null) + result.children.addAll(descriptionNodes) + return result } - public val description: ContentNode get() = sections["\$description"] ?: ContentNode.empty override fun equals(other: Any?): Boolean { if (other !is Content) return false - if (sections.size != other.sections.size) - return false - for (keys in sections.keySet()) - if (sections[keys] != other.sections[keys]) - return false - - return true + return sections == other.sections && children == other.children } override fun hashCode(): Int { @@ -110,7 +93,7 @@ public class Content() : ContentNode() { override fun toString(): String { if (sections.isEmpty()) return "" - return sections.values().joinToString() + return sections.joinToString() } val isEmpty: Boolean diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index a666b486..3a61b8fb 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -8,13 +8,11 @@ public open class DocumentationNode(val name: String, private val references = LinkedHashSet() + public val summary: ContentNode get() { - val contentSection = content.sections["\$summary"] + val contentSection = content.summary if (contentSection != null) return contentSection - val ownerSection = owner?.content?.sections?.get(name) - if (ownerSection != null) - return ownerSection return ContentNode.empty } diff --git a/src/main.kt b/src/main.kt index b075d4e9..9ebe0b1e 100644 --- a/src/main.kt +++ b/src/main.kt @@ -111,7 +111,7 @@ public fun main(args: Array) { if (file.exists()) { val text = file.readText() val tree = parseMarkdown(text) - val content = buildContent(tree, session.getPackageFragment(FqName.ROOT)) + val content = buildContent(tree) moduleContent.children.addAll(content.children) } else { println("WARN: Include file $file was not found.") diff --git a/test/data/comments/directive.kt b/test/data/comments/directive.kt index 9883b7bd..471ca9bb 100644 --- a/test/data/comments/directive.kt +++ b/test/data/comments/directive.kt @@ -1,10 +1,10 @@ /** * Summary * - * ${code example1} - * ${code example2} - * ${code X.example3} - * ${code X.Y.example4} + * @code example1 + * @code example2 + * @code X.example3 + * @code X.Y.example4 */ val property = "test" diff --git a/test/data/comments/emptySection.kt b/test/data/comments/emptySection.kt index 7112c578..47d6b1a5 100644 --- a/test/data/comments/emptySection.kt +++ b/test/data/comments/emptySection.kt @@ -1,6 +1,6 @@ /** * Summary - * $one: + * @one */ val property = "test" \ No newline at end of file diff --git a/test/data/comments/explicitSummary.kt b/test/data/comments/explicitSummary.kt deleted file mode 100644 index 68f75c99..00000000 --- a/test/data/comments/explicitSummary.kt +++ /dev/null @@ -1,6 +0,0 @@ - -/** - * $$summary: Summary - * $$description: Description - */ -val property = "test" \ No newline at end of file diff --git a/test/data/comments/multilineSection.kt b/test/data/comments/multilineSection.kt index 514348cd..6ef4df2c 100644 --- a/test/data/comments/multilineSection.kt +++ b/test/data/comments/multilineSection.kt @@ -1,6 +1,6 @@ /** * Summary - * $one: + * @one * line one * line two */ diff --git a/test/data/comments/section1.kt b/test/data/comments/section1.kt index e4c9adf4..7c763b4c 100644 --- a/test/data/comments/section1.kt +++ b/test/data/comments/section1.kt @@ -1,5 +1,5 @@ /** * Summary - * $one: section one + * @one section one */ val property = "test" \ No newline at end of file diff --git a/test/data/comments/section2.kt b/test/data/comments/section2.kt index e0a35551..e280793e 100644 --- a/test/data/comments/section2.kt +++ b/test/data/comments/section2.kt @@ -1,6 +1,6 @@ /** * Summary - * $one: section one - * $two: section two + * @one section one + * @two section two */ val property = "test" \ No newline at end of file diff --git a/test/data/comments/sectionWithBracedLabel.kt b/test/data/comments/sectionWithBracedLabel.kt deleted file mode 100644 index aa8dbfe6..00000000 --- a/test/data/comments/sectionWithBracedLabel.kt +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Summary - * ${this.label.is.really.long}: section one - */ -val property = "test" \ No newline at end of file diff --git a/test/data/format/paramTag.kt b/test/data/format/paramTag.kt new file mode 100644 index 00000000..47e471f5 --- /dev/null +++ b/test/data/format/paramTag.kt @@ -0,0 +1,6 @@ +/** + * @param x A string + * @param y A number with a really long description that spans multiple lines and goes + * on and on and is very interesting to read + */ +fun f(x: String, y: Int) {} diff --git a/test/data/format/paramTag.md b/test/data/format/paramTag.md new file mode 100644 index 00000000..9534deb3 --- /dev/null +++ b/test/data/format/paramTag.md @@ -0,0 +1,20 @@ +[test](out.md) / [](out.md) / [f](out.md) + + +# f + + +``` +fun f(x: String, y: Int): Unit +``` + + + + + + +### Parameters + +`x` - A string +`y` - A number with a really long description that spans multiple lines and goes +on and on and is very interesting to read diff --git a/test/data/format/throwsTag.kt b/test/data/format/throwsTag.kt new file mode 100644 index 00000000..29a9c3f7 --- /dev/null +++ b/test/data/format/throwsTag.kt @@ -0,0 +1,5 @@ +/** + * @throws IllegalArgumentException on Mondays + * @exception NullPointerException on Tuesdays + */ +fun f() {} diff --git a/test/data/format/throwsTag.md b/test/data/format/throwsTag.md new file mode 100644 index 00000000..d968483b --- /dev/null +++ b/test/data/format/throwsTag.md @@ -0,0 +1,19 @@ +[test](out.md) / [](out.md) / [f](out.md) + + +# f + + +``` +fun f(): Unit +``` + + + + + + +### Exceptions + +`IllegalArgumentException` - on Mondays +`NullPointerException` - on Tuesdays diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt index efde3139..77c03ed2 100644 --- a/test/src/format/MarkdownFormatTest.kt +++ b/test/src/format/MarkdownFormatTest.kt @@ -92,4 +92,16 @@ public class MarkdownFormatTest { markdownService.appendNodes(tempLocation, output, listOf(propertyNode.members[1])) } } + + Test fun paramTag() { + verifyOutput("test/data/format/paramTag.kt", ".md") { model, output -> + markdownService.appendNodes(tempLocation, output, model.members.single().members) + } + } + + Test fun throwsTag() { + verifyOutput("test/data/format/throwsTag.kt", ".md") { model, output -> + markdownService.appendNodes(tempLocation, output, model.members.single().members) + } + } } diff --git a/test/src/model/CommentTest.kt b/test/src/model/CommentTest.kt index 828deaa4..353336d6 100644 --- a/test/src/model/CommentTest.kt +++ b/test/src/model/CommentTest.kt @@ -67,8 +67,8 @@ public class CommentTest { verifyModel("test/data/comments/emptySection.kt") { model -> with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.sections["one"]!!) { + assertEquals(1, content.sections.count()) + with (content.findSectionByName("one")!!) { assertEquals("one", label) assertEquals("", toTestString()) } @@ -76,22 +76,12 @@ public class CommentTest { } } - Test fun explicitSummary() { - verifyModel("test/data/comments/explicitSummary.kt") { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals("Description", content.description.toTestString()) - assertEquals(2, content.sections.count()) - } - } - } - Test fun section1() { verifyModel("test/data/comments/section1.kt") { model -> with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.sections["one"]!!) { + assertEquals(1, content.sections.count()) + with (content.findSectionByName("one")!!) { assertEquals("one", label) assertEquals("section one", toTestString()) } @@ -103,12 +93,12 @@ public class CommentTest { verifyModel("test/data/comments/section2.kt") { model -> with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) - assertEquals(3, content.sections.count()) - with (content.sections["one"]!!) { + assertEquals(2, content.sections.count()) + with (content.findSectionByName("one")!!) { assertEquals("one", label) assertEquals("section one", toTestString()) } - with (content.sections["two"]!!) { + with (content.findSectionByName("two")!!) { assertEquals("two", label) assertEquals("section two", toTestString()) } @@ -120,8 +110,8 @@ public class CommentTest { verifyModel("test/data/comments/multilineSection.kt") { model -> with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.sections["one"]!!) { + assertEquals(1, content.sections.count()) + with (content.findSectionByName("one")!!) { assertEquals("one", label) assertEquals("""line one line two""", toTestString()) @@ -130,43 +120,26 @@ line two""", toTestString()) } } - Test fun sectionWithBracedLabel() { - verifyModel("test/data/comments/sectionWithBracedLabel.kt") { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.sections["this.label.is.really.long"]!!) { - assertEquals("this.label.is.really.long", label) - assertEquals("section one", toTestString()) - } - } - } - } - Test fun directive() { verifyModel("test/data/comments/directive.kt") { model -> with(model.members.single().members.first()) { assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) with (content.description) { assertEqualsIgnoringSeparators("""[code] if (true) { println(property) } [/code] - [code] if (true) { println(property) } [/code] - [code] if (true) { println(property) } [/code] - [code] if (true) { println(property) -- cgit From 0fac1d925b74f24002a4e1538088ce66c4b02cb9 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 30 Jan 2015 19:01:40 +0100 Subject: code review --- src/Formats/StructuredFormatService.kt | 21 +++++++++++---------- src/Kotlin/ContentBuilder.kt | 2 +- src/Kotlin/DocumentationBuilder.kt | 14 +++++++++----- src/Model/Content.kt | 24 ++++++++++++------------ src/Model/DocumentationNode.kt | 8 +------- test/src/model/CommentTest.kt | 20 ++++++++++---------- 6 files changed, 44 insertions(+), 45 deletions(-) diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index fe127732..6ec75379 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -98,23 +98,24 @@ public abstract class StructuredFormatService(val locationService: LocationServi appendSectionWithSubject(it.getKey(), location, it.getValue(), to) } - for (section in node.content.sections) { - if (section.subjectName != null) continue - appendLine(to, formatStrong(formatText(section.label))) + for (section in node.content.sections.filter { it.subjectName == null }) { + appendLine(to, formatStrong(formatText(section.tag))) appendLine(to, formatText(location, section)) } } } } - fun appendSectionWithSubject(title: String, location: Location, parameterSections: List, to: StringBuilder) { + fun Content.getSectionsWithSubjects(): Map> = + sections.filter { it.subjectName != null }.groupBy { it.tag } + + fun appendSectionWithSubject(title: String, location: Location, subjectSections: List, to: StringBuilder) { appendHeader(to, title, 3) - parameterSections.forEach { - val parameterName = it.subjectName - if (parameterName != null) { - to.append(formatCode(parameterName)).append(" - ") - val formatted = formatText(location, it) - to.append(formatted) + subjectSections.forEach { + val subjectName = it.subjectName + if (subjectName != null) { + to.append(formatCode(subjectName)).append(" - ") + to.append(formatText(location, it)) appendLine(to) } } diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt index 95e08c92..35beec54 100644 --- a/src/Kotlin/ContentBuilder.kt +++ b/src/Kotlin/ContentBuilder.kt @@ -112,7 +112,7 @@ public fun DocumentationBuilder.buildContentTo(tree: MarkdownNode, target: Conte private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection public fun DocumentationBuilder.buildInlineContentTo(tree: MarkdownNode, target: ContentNode) { - val inlineContent = tree.children.firstOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) + val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) inlineContent.forEach { buildContentTo(it, target) } diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index 053615bc..c0533437 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -41,13 +41,15 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati fun parseDocumentation(descriptor: DeclarationDescriptor): Content { val kdoc = findKDoc(descriptor) - val docText = kdoc?.getContent() ?: "" - val tree = parseMarkdown(docText) + if (kdoc == null) { + return Content.Empty + } + val tree = parseMarkdown(kdoc.getContent()) //println(tree.toTestString()) val content = buildContent(tree) if (kdoc is KDocSection) { - val tags = PsiTreeUtil.getChildrenOfType(kdoc, javaClass()) - tags?.forEach { + val tags = kdoc.getTags() + tags.forEach { if (it.getName() == "code") { content.append(functionBody(descriptor, it.getContent())) } else { @@ -61,11 +63,13 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati return content } + fun KDocSection.getTags(): Array = PsiTreeUtil.getChildrenOfType(this, javaClass()) ?: array() + fun displayName(sectionName: String?): String? = when(sectionName) { "param" -> "Parameters" "throws", "exception" -> "Exceptions" - else -> sectionName + else -> sectionName?.capitalize() } fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) { diff --git a/src/Model/Content.kt b/src/Model/Content.kt index 2fae317d..e59037d2 100644 --- a/src/Model/Content.kt +++ b/src/Model/Content.kt @@ -1,5 +1,7 @@ package org.jetbrains.dokka +import kotlin.properties.Delegates + public abstract class ContentNode { val children = arrayListOf() @@ -32,7 +34,7 @@ public class ContentNodeLink(val node : DocumentationNode) : ContentBlock() public class ContentExternalLink(val href : String) : ContentBlock() public class ContentList() : ContentBlock() public class ContentListItem() : ContentBlock() -public class ContentSection(public val label: String, public val subjectName: String?) : ContentBlock() +public class ContentSection(public val tag: String, public val subjectName: String?) : ContentBlock() fun content(body: ContentNode.() -> Unit): ContentNode { val block = ContentBlock() @@ -62,22 +64,20 @@ public class Content() : ContentNode() { return section } - fun findSectionByName(name: String): ContentSection? = - sections.firstOrNull { it.label == name } - - fun getSectionsWithSubjects(): Map> = - sections.filter { it.subjectName != null }.groupBy { it.label } + fun findSectionByTag(tag: String): ContentSection? = + sections.firstOrNull { tag.equalsIgnoreCase(it.tag) } public val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty - public val description: ContentNode get() { + public val description: ContentNode by Delegates.lazy { val descriptionNodes = children.drop(1) if (descriptionNodes.isEmpty()) { - return ContentEmpty + ContentEmpty + } else { + val result = ContentSection("Description", null) + result.children.addAll(descriptionNodes) + result } - val result = ContentSection("\$description", null) - result.children.addAll(descriptionNodes) - return result } override fun equals(other: Any?): Boolean { @@ -93,7 +93,7 @@ public class Content() : ContentNode() { override fun toString(): String { if (sections.isEmpty()) return "" - return sections.joinToString() + return (listOf(summary, description) + sections).joinToString() } val isEmpty: Boolean diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index 3a61b8fb..c3700069 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -8,13 +8,7 @@ public open class DocumentationNode(val name: String, private val references = LinkedHashSet() - - public val summary: ContentNode get() { - val contentSection = content.summary - if (contentSection != null) - return contentSection - return ContentNode.empty - } + public val summary: ContentNode get() = content.summary public val owner: DocumentationNode? get() = references(DocumentationReference.Kind.Owner).singleOrNull()?.to diff --git a/test/src/model/CommentTest.kt b/test/src/model/CommentTest.kt index 353336d6..98585b18 100644 --- a/test/src/model/CommentTest.kt +++ b/test/src/model/CommentTest.kt @@ -68,8 +68,8 @@ public class CommentTest { with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) assertEquals(1, content.sections.count()) - with (content.findSectionByName("one")!!) { - assertEquals("one", label) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) assertEquals("", toTestString()) } } @@ -81,8 +81,8 @@ public class CommentTest { with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) assertEquals(1, content.sections.count()) - with (content.findSectionByName("one")!!) { - assertEquals("one", label) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) assertEquals("section one", toTestString()) } } @@ -94,12 +94,12 @@ public class CommentTest { with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) assertEquals(2, content.sections.count()) - with (content.findSectionByName("one")!!) { - assertEquals("one", label) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) assertEquals("section one", toTestString()) } - with (content.findSectionByName("two")!!) { - assertEquals("two", label) + with (content.findSectionByTag("two")!!) { + assertEquals("Two", tag) assertEquals("section two", toTestString()) } } @@ -111,8 +111,8 @@ public class CommentTest { with(model.members.single().members.single()) { assertEquals("Summary", content.summary.toTestString()) assertEquals(1, content.sections.count()) - with (content.findSectionByName("one")!!) { - assertEquals("one", label) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) assertEquals("""line one line two""", toTestString()) } -- cgit