From bd6cddd932c308519ce386197b93de145462bec2 Mon Sep 17 00:00:00 2001 From: Ilya Ryzhenkov Date: Tue, 16 Dec 2014 21:41:32 +0300 Subject: Process short links. --- dokka.iml | 2 +- src/Kotlin/ContentBuilder.kt | 64 +++++++++++++++++-------------- src/Kotlin/DocumentationBuilder.kt | 52 +++++++++++++++---------- src/Markdown/MarkdownProcessor.kt | 33 +++++++++------- test/data/functions/functionWithParams.kt | 1 + test/data/links/linkToJDK.kt | 0 test/data/links/linkToMember.kt | 6 +++ test/data/links/linkToParam.kt | 5 +++ test/data/links/linkToQualifiedMember.kt | 6 +++ test/data/links/linkToSelf.kt | 6 +++ test/src/TestAPI.kt | 7 ++++ test/src/markdown/ParserTest.kt | 20 ++++++---- test/src/model/CommentTest.kt | 2 +- test/src/model/FunctionTest.kt | 30 +++++++-------- test/src/model/LinkTest.kt | 48 +++++++++++++++++++++++ test/src/model/PropertyTest.kt | 4 +- 16 files changed, 198 insertions(+), 88 deletions(-) create mode 100644 test/data/links/linkToJDK.kt create mode 100644 test/data/links/linkToMember.kt create mode 100644 test/data/links/linkToParam.kt create mode 100644 test/data/links/linkToQualifiedMember.kt create mode 100644 test/data/links/linkToSelf.kt create mode 100644 test/src/model/LinkTest.kt diff --git a/dokka.iml b/dokka.iml index 509dfa7e..b0393b73 100644 --- a/dokka.iml +++ b/dokka.iml @@ -13,6 +13,6 @@ - + \ No newline at end of file diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt index 8079fb4c..462e886e 100644 --- a/src/Kotlin/ContentBuilder.kt +++ b/src/Kotlin/ContentBuilder.kt @@ -5,17 +5,16 @@ import org.jetbrains.jet.lang.descriptors.* import org.jetbrains.jet.lang.resolve.* import org.jetbrains.jet.lang.resolve.scopes.* import org.jetbrains.jet.lang.resolve.name.* -import net.nicoulaj.idea.markdown.lang.* +import org.intellij.markdown.* public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: DeclarationDescriptor): Content { +// println(tree.toTestString()) val nodeStack = ArrayDeque() nodeStack.push(Content()) tree.visit {(node, processChildren) -> val parent = nodeStack.peek()!! - val nodeType = node.type - val nodeText = tree.text - when (nodeType) { + when (node.type) { MarkdownElementTypes.UNORDERED_LIST -> { nodeStack.push(ContentList()) processChildren() @@ -46,33 +45,40 @@ public fun DocumentationBuilder.buildContent(tree: MarkdownNode, descriptor: Dec processChildren() parent.append(nodeStack.pop()) } - /* MarkdownElementTypes.ANONYMOUS_SECTION -> { - nodeStack.push(ContentSection("")) - processChildren() - parent.append(nodeStack.pop()) - } - MarkdownElementTypes.DIRECTIVE -> { - val name = tree.findChildByType(node, MarkdownElementTypes.DIRECTIVE_NAME)?.let { tree.getNodeText(it) } ?: "" - val params = tree.findChildByType(node, MarkdownElementTypes.DIRECTIVE_PARAMS)?.let { tree.getNodeText(it) } ?: "" - when (name) { - "code" -> parent.append(functionBody(descriptor, params)) - } - } - MarkdownElementTypes.NAMED_SECTION -> { - val label = tree.findChildByType(node, MarkdownElementTypes.SECTION_NAME)?.let { tree.getNodeText(it) } ?: "" - nodeStack.push(ContentSection(label)) - processChildren() - parent.append(nodeStack.pop()) - }*/ - MarkdownElementTypes.INLINE_LINK -> { - val target = node.child(MarkdownElementTypes.LINK_TITLE)?.let { it.text } ?: "" - val href = node.child(MarkdownElementTypes.LINK_DESTINATION)?.let { it.text } - val link = if (href != null) ContentExternalLink(href) else ContentExternalLink(target) - link.append(ContentText(target)) - parent.append(link) +/* + MarkdownElementTypes.DIRECTIVE -> { + val name = tree.findChildByType(node, MarkdownElementTypes.DIRECTIVE_NAME)?.let { tree.getNodeText(it) } ?: "" + val params = tree.findChildByType(node, MarkdownElementTypes.DIRECTIVE_PARAMS)?.let { tree.getNodeText(it) } ?: "" + 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.SHORT_REFERENCE_LINK -> { + val label = node.child(MarkdownElementTypes.LINK_LABEL) + val target = label?.child(MarkdownTokenTypes.TEXT) + if (target != null) { + val link = ContentExternalLink(target.text) + link.append(ContentText(target.text)) + parent.append(link) + } + } + MarkdownTokenTypes.WHITE_SPACE, + MarkdownTokenTypes.EOL -> { + if (nodeStack.peek() is ContentParagraph && node.parent?.children?.last() != node) { + nodeStack.push(ContentText(node.text)) + processChildren() + parent.append(nodeStack.pop()) + } } MarkdownTokenTypes.TEXT -> { - nodeStack.push(ContentText(nodeText)) + nodeStack.push(ContentText(node.text)) processChildren() parent.append(nodeStack.pop()) } diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index d8be9d5a..c2d28312 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -4,7 +4,6 @@ import org.jetbrains.jet.lang.descriptors.* import org.jetbrains.dokka.DocumentationNode.* import org.jetbrains.jet.lang.types.* import org.jetbrains.jet.lang.types.lang.* -import org.jetbrains.jet.lang.resolve.scopes.* import org.jetbrains.jet.lang.resolve.name.* import org.jetbrains.jet.lang.resolve.lazy.* @@ -218,6 +217,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati fun ValueParameterDescriptor.build(): DocumentationNode { val node = DocumentationNode(this, Kind.Parameter) node.appendType(getType()) + register(this, node) return node } @@ -301,9 +301,9 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati } } - fun getResolutionScope(node: DocumentationNode): JetScope { + fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor { val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context") - return getResolutionScope(descriptor) + return descriptor } fun resolveContentLinks(node: DocumentationNode, content: ContentNode) { @@ -311,26 +311,38 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati for (child in snapshot) { if (child is ContentExternalLink) { val referenceText = child.href - if (Name.isValidIdentifier(referenceText)) { - val scope = getResolutionScope(node) - val symbolName = Name.guess(referenceText) - val symbol = scope.getLocalVariable(symbolName) ?: - scope.getProperties(symbolName).firstOrNull() ?: - scope.getFunctions(symbolName).firstOrNull() ?: - scope.getClassifier(symbolName) - - if (symbol != null) { - val targetNode = descriptorToNode[symbol] - val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#") - - val index = content.children.indexOf(child) - content.children.remove(index) - contentLink.children.addAll(child.children) - content.children.add(index, contentLink) - } + val symbol = resolveReference(getResolutionScope(node), referenceText) + if (symbol != null) { + val targetNode = descriptorToNode[symbol] + val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#") + + val index = content.children.indexOf(child) + content.children.remove(index) + contentLink.children.addAll(child.children) + content.children.add(index, contentLink) } + } resolveContentLinks(node, child) } } + + private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? { + if (Name.isValidIdentifier(reference)) { + val scope = getResolutionScope(context) + val symbolName = Name.guess(reference) + return scope.getLocalVariable(symbolName) ?: + scope.getProperties(symbolName).firstOrNull() ?: + scope.getFunctions(symbolName).firstOrNull() ?: + scope.getClassifier(symbolName) + + } + + val names = reference.split('.') + val result = names.fold(context) {(nextContext, name) -> + nextContext?.let { resolveReference(it, name) } + } + + return result + } } \ No newline at end of file diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt index b5e18f92..05c4a7ec 100644 --- a/src/Markdown/MarkdownProcessor.kt +++ b/src/Markdown/MarkdownProcessor.kt @@ -1,17 +1,19 @@ package org.jetbrains.dokka -import net.nicoulaj.idea.markdown.lang.ast.* -import net.nicoulaj.idea.markdown.lang.parser.dialects.commonmark.* -import net.nicoulaj.idea.markdown.lang.parser.* -import net.nicoulaj.idea.markdown.lang.* +import org.intellij.markdown.* +import org.intellij.markdown.ast.* +import org.intellij.markdown.parser.* +import org.intellij.markdown.parser.dialects.KDocMarkerProcessor -class MarkdownNode(val node: ASTNode, val markdown: String) { - val children: List get() = node.children.map { MarkdownNode(it, markdown) } +class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { + val children: List = node.children.map { MarkdownNode(it, this, markdown) } val endOffset: Int get() = node.endOffset val startOffset: Int get() = node.startOffset val type: IElementType get() = node.type val text: String get() = markdown.substring(startOffset, endOffset) fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } + + override fun toString(): String = present() } fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) { @@ -27,9 +29,7 @@ public fun MarkdownNode.toTestString(): String { var level = 0 visit {(node, visitChildren) -> sb.append(" ".repeat(level * 2)) - sb.append(node.type.toString()) - sb.append(":" + node.text.replace("\n", "\u23CE")) - sb.appendln() + node.presentTo(sb) level++ visitChildren() level-- @@ -37,6 +37,13 @@ public fun MarkdownNode.toTestString(): String { return sb.toString() } +private fun MarkdownNode.present() = StringBuilder { presentTo(this) }.toString() +private fun MarkdownNode.presentTo(sb: StringBuilder) { + sb.append(type.toString()) + sb.append(":" + text.replace("\n", "\u23CE")) + sb.appendln() +} + public fun MarkdownNode.toHtml(): String { val sb = StringBuilder() visit {(node, processChildren) -> @@ -126,14 +133,14 @@ public fun MarkdownNode.toHtml(): String { fun parseMarkdown(markdown: String): MarkdownNode { if (markdown.isEmpty()) - return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), markdown) - return MarkdownNode(MarkdownParser(CommonMarkMarkerProcessor()).buildMarkdownTreeFromString(markdown), markdown) + return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown) + return MarkdownNode(MarkdownParser(KDocMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown), null, markdown) } fun markdownToHtml(markdown: String): String { - val tree = MarkdownParser(CommonMarkMarkerProcessor()).buildMarkdownTreeFromString(markdown) - val markdownTree = MarkdownNode(tree, markdown) + val tree = MarkdownParser(KDocMarkerProcessor.Factory()).buildMarkdownTreeFromString(markdown) + val markdownTree = MarkdownNode(tree, null, markdown) val ast = markdownTree.toTestString() return markdownTree.toHtml() } diff --git a/test/data/functions/functionWithParams.kt b/test/data/functions/functionWithParams.kt index 52cd0744..85c49368 100644 --- a/test/data/functions/functionWithParams.kt +++ b/test/data/functions/functionWithParams.kt @@ -1,5 +1,6 @@ /** * Multiline + * * Function * Documentation */ diff --git a/test/data/links/linkToJDK.kt b/test/data/links/linkToJDK.kt new file mode 100644 index 00000000..e69de29b diff --git a/test/data/links/linkToMember.kt b/test/data/links/linkToMember.kt new file mode 100644 index 00000000..b60eaedb --- /dev/null +++ b/test/data/links/linkToMember.kt @@ -0,0 +1,6 @@ +/** + * This is link to [member] + */ +class Foo { + fun member() {} +} \ No newline at end of file diff --git a/test/data/links/linkToParam.kt b/test/data/links/linkToParam.kt new file mode 100644 index 00000000..ca42a742 --- /dev/null +++ b/test/data/links/linkToParam.kt @@ -0,0 +1,5 @@ +/** + * This is link to [param] + */ +fun Foo(param: String) { +} \ No newline at end of file diff --git a/test/data/links/linkToQualifiedMember.kt b/test/data/links/linkToQualifiedMember.kt new file mode 100644 index 00000000..22c154fe --- /dev/null +++ b/test/data/links/linkToQualifiedMember.kt @@ -0,0 +1,6 @@ +/** + * This is link to [Foo.member] + */ +class Foo { + fun member() {} +} \ No newline at end of file diff --git a/test/data/links/linkToSelf.kt b/test/data/links/linkToSelf.kt new file mode 100644 index 00000000..74395f0f --- /dev/null +++ b/test/data/links/linkToSelf.kt @@ -0,0 +1,6 @@ +/** + * This is link to [Foo] + */ +class Foo { + +} \ No newline at end of file diff --git a/test/src/TestAPI.kt b/test/src/TestAPI.kt index ccef8919..5a4b9863 100644 --- a/test/src/TestAPI.kt +++ b/test/src/TestAPI.kt @@ -63,6 +63,13 @@ fun StringBuilder.appendNode(node: ContentNode): StringBuilder { append(node.text) } is ContentEmphasis -> append("*").appendChildren(node).append("*") + is ContentNodeLink -> { + append("[") + appendChildren(node) + append(" -> ") + append(node.node.toString()) + append("]") + } else -> { appendChildren(node) } diff --git a/test/src/markdown/ParserTest.kt b/test/src/markdown/ParserTest.kt index 16892f23..80ee7332 100644 --- a/test/src/markdown/ParserTest.kt +++ b/test/src/markdown/ParserTest.kt @@ -5,12 +5,18 @@ import org.jetbrains.dokka import org.jetbrains.dokka.toTestString import org.jetbrains.dokka.toHtml import org.jetbrains.dokka.parseMarkdown +import org.junit.Ignore -public class ParserTest { +Ignore public class ParserTest { fun runTestFor(text : String) { + println("MD: ---") + println(text) val markdownTree = parseMarkdown(text) + println("AST: ---") println(markdownTree.toTestString()) + println("HTML: ---") println(markdownTree.toHtml()) + println() } Test fun text() { @@ -89,20 +95,20 @@ number two } Test fun emphAndEmptySection() { - runTestFor("*text* \$sec:") + runTestFor("*text*\n\$sec:\n") } Test fun emphAndSection() { - runTestFor("*text* \$sec: some text") + runTestFor("*text*\n\$sec: some text\n") } Test fun emphAndBracedSection() { - runTestFor("Text *bold* text \${sec}: some text") + runTestFor("Text *bold* text \n\${sec}: some text") } Test fun section() { runTestFor( - "Plain text \$one: Summary \${two}: Description with *emphasis* \${An example of a section}: Example") + "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example") } Test fun anonymousSection() { @@ -111,12 +117,12 @@ number two Test fun specialSection() { runTestFor( - "Plain text \$\$summary: Summary \${\$description}: Description \${\$An example of a section}: Example") + "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example") } Test fun emptySection() { runTestFor( - "Plain text \$summary:") + "Plain text \n\$summary:") } val b = "$" diff --git a/test/src/model/CommentTest.kt b/test/src/model/CommentTest.kt index b2ad0ac0..5fc95726 100644 --- a/test/src/model/CommentTest.kt +++ b/test/src/model/CommentTest.kt @@ -25,7 +25,7 @@ public class CommentTest { verifyModel("test/data/comments/multilineDoc.kt") { model -> with(model.members.single().members.single()) { assertEquals("doc1", content.summary.toTestString()) - assertEquals("doc2\ndoc3\n", content.description.toTestString()) + assertEquals("doc2\ndoc3", content.description.toTestString()) } } } diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt index c6ad93cc..2a4ad0a5 100644 --- a/test/src/model/FunctionTest.kt +++ b/test/src/model/FunctionTest.kt @@ -24,12 +24,10 @@ public class FunctionTest { assertEquals("fn", name) assertEquals(DocumentationNode.Kind.Function, kind) assertEquals("Function with receiver", content.summary.toTestString()) - assertEquals("Unit", details.elementAt(0).name) - assertEquals(4, details.count()) + assertEquals("internal", details.elementAt(0).name) assertEquals("final", details.elementAt(1).name) - assertEquals("internal", details.elementAt(2).name) - with(details.elementAt(3)) { + with(details.elementAt(2)) { assertEquals("", name) assertEquals(DocumentationNode.Kind.Receiver, kind) assertEquals(Content.Empty, content) @@ -37,6 +35,7 @@ public class FunctionTest { assertTrue(members.none()) assertTrue(links.none()) } + assertEquals("Unit", details.elementAt(3).name) assertTrue(members.none()) assertTrue(links.none()) } @@ -51,10 +50,9 @@ public class FunctionTest { assertEquals("generic function", content.summary.toTestString()) assertEquals(4, details.count()) - assertEquals("Unit", details.elementAt(0).name) + assertEquals("private", details.elementAt(0).name) assertEquals("final", details.elementAt(1).name) - assertEquals("private", details.elementAt(2).name) - with(details.elementAt(3)) { + with(details.elementAt(2)) { assertEquals("T", name) assertEquals(DocumentationNode.Kind.TypeParameter, kind) assertEquals(Content.Empty, content) @@ -62,6 +60,7 @@ public class FunctionTest { assertTrue(members.none()) assertTrue(links.none()) } + assertEquals("Unit", details.elementAt(3).name) assertTrue(members.none()) assertTrue(links.none()) @@ -76,14 +75,15 @@ public class FunctionTest { assertEquals("generic function", content.summary.toTestString()) assertEquals(5, details.count()) - assertEquals("Unit", details.elementAt(0).name) + assertEquals("public", details.elementAt(0).name) assertEquals("final", details.elementAt(1).name) - assertEquals("public", details.elementAt(2).name) - with(details.elementAt(3)) { + with(details.elementAt(2)) { assertEquals("T", name) assertEquals(DocumentationNode.Kind.TypeParameter, kind) assertEquals(Content.Empty, content) with(details.single()) { + assertEquals("R", name) + assertEquals("R", name) assertEquals("R", name) assertEquals(DocumentationNode.Kind.UpperBound, kind) assertEquals(Content.Empty, content) @@ -94,13 +94,14 @@ public class FunctionTest { assertTrue(members.none()) assertTrue(links.none()) } - with(details.elementAt(4)) { + with(details.elementAt(3)) { assertEquals("R", name) assertEquals(DocumentationNode.Kind.TypeParameter, kind) assertEquals(Content.Empty, content) assertTrue(members.none()) assertTrue(links.none()) } + assertEquals("Unit", details.elementAt(4).name) assertTrue(members.none()) assertTrue(links.none()) @@ -118,10 +119,9 @@ public class FunctionTest { Documentation""", content.description.toTestString()) assertEquals(4, details.count()) - assertEquals("Unit", details.elementAt(0).name) + assertEquals("internal", details.elementAt(0).name) assertEquals("final", details.elementAt(1).name) - assertEquals("internal", details.elementAt(2).name) - with(details.elementAt(3)) { + with(details.elementAt(2)) { assertEquals("x", name) assertEquals(DocumentationNode.Kind.Parameter, kind) assertEquals("parameter", content.summary.toTestString()) @@ -129,7 +129,7 @@ Documentation""", content.description.toTestString()) assertTrue(members.none()) assertTrue(links.none()) } - + assertEquals("Unit", details.elementAt(3).name) assertTrue(members.none()) assertTrue(links.none()) } diff --git a/test/src/model/LinkTest.kt b/test/src/model/LinkTest.kt new file mode 100644 index 00000000..151d2696 --- /dev/null +++ b/test/src/model/LinkTest.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.tests + +import org.junit.Test +import kotlin.test.* +import org.jetbrains.dokka.* + +public class LinkTest { + Test fun linkToSelf() { + verifyModel("test/data/links/linkToSelf.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(DocumentationNode.Kind.Class, kind) + assertEquals("This is link to [Foo -> Class:Foo]", content.summary.toTestString()) + } + } + } + + Test fun linkToMember() { + verifyModel("test/data/links/linkToMember.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(DocumentationNode.Kind.Class, kind) + assertEquals("This is link to [member -> Function:member]", content.summary.toTestString()) + } + } + } + + Test fun linkToQualifiedMember() { + verifyModel("test/data/links/linkToQualifiedMember.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(DocumentationNode.Kind.Class, kind) + assertEquals("This is link to [Foo.member -> Function:member]", content.summary.toTestString()) + } + } + } + + Test fun linkToParam() { + verifyModel("test/data/links/linkToParam.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(DocumentationNode.Kind.Function, kind) + assertEquals("This is link to [param -> Parameter:param]", content.summary.toTestString()) + } + } + } + +} \ No newline at end of file diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt index 93d81769..7cd287a0 100644 --- a/test/src/model/PropertyTest.kt +++ b/test/src/model/PropertyTest.kt @@ -40,7 +40,7 @@ public class PropertyTest { assertEquals("String", detail(DocumentationNode.Kind.Type).name) assertTrue(links.none()) with(members.single()) { - assertEquals("", name) + assertEquals("", name) assertEquals(DocumentationNode.Kind.Function, kind) assertEquals(Content.Empty, content) assertEquals("String", detail(DocumentationNode.Kind.Type).name) @@ -66,7 +66,7 @@ public class PropertyTest { assertEquals(2, members.count()) with(members.elementAt(0)) { - assertEquals("", name) + assertEquals("", name) assertEquals(DocumentationNode.Kind.Function, kind) assertEquals(Content.Empty, content) val get_modifiers = details(DocumentationNode.Kind.Modifier).map { it.name } -- cgit