From 5ab32ba0dd61585459bb32bb31fe7b19493c980e Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 28 Nov 2016 21:42:39 +0300 Subject: Fix for GH-115, `@param` and `@return` tags is missing in javadoc output --- core/src/main/kotlin/Model/Content.kt | 1 + core/src/main/kotlin/javadoc/docbase.kt | 54 +++++++++++++++++++++------ core/src/main/kotlin/javadoc/tags.kt | 19 ++++++++-- core/src/test/kotlin/javadoc/JavadocTest.kt | 7 ++++ core/testdata/javadoc/kdocKeywordsOnMethod.kt | 7 ++++ 5 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 core/testdata/javadoc/kdocKeywordsOnMethod.kt (limited to 'core') diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 0a38a524..7464d510 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -131,6 +131,7 @@ class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() object ContentTags { val Description = "Description" val SeeAlso = "See Also" + val Return = "Return" } fun content(body: ContentBlock.() -> Unit): ContentBlock { diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt index 20d74088..b125a7d4 100644 --- a/core/src/main/kotlin/javadoc/docbase.kt +++ b/core/src/main/kotlin/javadoc/docbase.kt @@ -297,6 +297,10 @@ fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode node } +private fun DocumentationNode.hasNonEmptyContent() = + this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty() + + open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc by ProgramElementAdapter(module, node), ExecutableMemberDoc { override fun isSynthetic(): Boolean = false @@ -315,10 +319,15 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" } - override fun paramTags(): Array = node.details(NodeKind.Parameter) - .filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() } - .map { ParamTagAdapter(module, this, it.name, false, it.content.children) } - .toTypedArray() + override fun paramTags(): Array = + (node.details(NodeKind.Parameter) + .filter(DocumentationNode::hasNonEmptyContent) + .map { ParamTagAdapter(module, this, it.name, false, it.content.children) } + + + node.content.sections + .filter { it.subjectName in parameters().map { it.name() } } + .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } + ).toTypedArray() override fun thrownExceptionTypes(): Array = emptyArray() override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) } @@ -332,9 +341,15 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio override fun typeParameters(): Array = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() - override fun typeParamTags(): Array = node.details(NodeKind.TypeParameter).filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() }.map { - ParamTagAdapter(module, this, it.name, true, it.content.children) - }.toTypedArray() + override fun typeParamTags(): Array = + (node.details(NodeKind.TypeParameter) + .filter(DocumentationNode::hasNonEmptyContent) + .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } + + + node.content.sections + .filter { it.subjectName in typeParameters().map { it.simpleTypeName() } } + .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } + ).toTypedArray() private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers -> when { @@ -365,6 +380,17 @@ class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : Docume override fun isDefault(): Boolean = false override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) + + override fun tags(tagname: String?) = super.tags(tagname) + + override fun tags(): Array { + val tags = super.tags().toMutableList() + node.content.findSectionByTag(ContentTags.Return)?.let { + tags += ReturnTagAdapter(module, this, it.children) + } + + return tags.toTypedArray() + } } class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc by ProgramElementAdapter(module, node), FieldDoc { @@ -416,11 +442,15 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod .map { ClassDocumentationNodeAdapter(module, it) } .toTypedArray() - override fun typeParamTags(): Array = (classNode.details(NodeKind.TypeParameter).filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() }.map { - ParamTagAdapter(module, this, it.name, true, it.content.children) - } + classNode.content.sections.filter { it.subjectName in typeParameters().map { it.simpleTypeName() } }.map { - ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) - }).toTypedArray() + override fun typeParamTags(): Array = + (classNode.details(NodeKind.TypeParameter) + .filter(DocumentationNode::hasNonEmptyContent) + .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } + + + classNode.content.sections + .filter { it.subjectName in typeParameters().map { it.simpleTypeName() } } + .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } + ).toTypedArray() override fun fields(): Array = fields(true) override fun fields(filter: Boolean): Array = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray() diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt index aab5ecf1..5ee2cbac 100644 --- a/core/src/main/kotlin/javadoc/tags.kt +++ b/core/src/main/kotlin/javadoc/tags.kt @@ -104,8 +104,8 @@ class ParamTagAdapter(val module: ModuleNodeAdapter, override fun holder(): Doc = holder override fun position(): SourcePosition? = holder.position() - override fun text(): String = "@param $parameterName ..." - override fun inlineTags(): Array = content.flatMap { buildInlineTags(module, holder, it) }.toTypedArray() + override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug + override fun inlineTags(): Array = buildInlineTags(module, holder, content).toTypedArray() override fun firstSentenceTags(): Array = arrayOf(TextTag(holder, ContentText(text()))) override fun isTypeParameter(): Boolean = typeParameter @@ -130,7 +130,20 @@ class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter) override fun exceptionName(): String = type.qualifiedName() } -fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List = ArrayList().let { buildInlineTags(module, holder, root, it); it } +class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List) : Tag { + override fun name(): String = "@return" + override fun kind() = name() + override fun holder() = holder + override fun position(): SourcePosition? = holder.position() + + override fun text(): String = "@return $content" // Seems has no effect, so used for debug + override fun inlineTags(): Array = buildInlineTags(module, holder, content).toTypedArray() + override fun firstSentenceTags(): Array = inlineTags() +} + +fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List): List = ArrayList().apply { tags.forEach { buildInlineTags(module, holder, it, this) } } + +fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List = ArrayList().apply { buildInlineTags(module, holder, root, this) } private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List, result: MutableList) { nodes.forEach { diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt index 4c0f7f0f..f60c49fd 100644 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -123,6 +123,13 @@ class JavadocTest { } } + @Test fun testKDocKeywordsOnMethod() { + verifyJavadoc("testdata/javadoc/kdocKeywordsOnMethod.kt", withKotlinRuntime = true) { doc -> + val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0] + assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text()) + assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text()) + } + } private fun verifyJavadoc(name: String, withJdk: Boolean = false, diff --git a/core/testdata/javadoc/kdocKeywordsOnMethod.kt b/core/testdata/javadoc/kdocKeywordsOnMethod.kt new file mode 100644 index 00000000..63bbaba4 --- /dev/null +++ b/core/testdata/javadoc/kdocKeywordsOnMethod.kt @@ -0,0 +1,7 @@ +/** + * COMM + * @param a Some string + * @return value of a + */ +fun my(a: String): String = a + -- cgit From f3dd7eb1d36913920e431c88a633cf5c5e098820 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 29 Nov 2016 15:28:39 +0300 Subject: Post-review fixes, removed code duplication, fixed @throws support --- core/src/main/kotlin/Model/Content.kt | 3 +- core/src/main/kotlin/javadoc/docbase.kt | 44 ++++++++++----------------- core/src/main/kotlin/javadoc/tags.kt | 10 +++--- core/src/test/kotlin/javadoc/JavadocTest.kt | 1 + core/testdata/javadoc/kdocKeywordsOnMethod.kt | 5 +++ 5 files changed, 29 insertions(+), 34 deletions(-) (limited to 'core') diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt index 7464d510..1a8bc5d2 100644 --- a/core/src/main/kotlin/Model/Content.kt +++ b/core/src/main/kotlin/Model/Content.kt @@ -132,6 +132,7 @@ object ContentTags { val Description = "Description" val SeeAlso = "See Also" val Return = "Return" + val Exceptions = "Exceptions" } fun content(body: ContentBlock.() -> Unit): ContentBlock { @@ -237,6 +238,6 @@ open class MutableContent() : Content() { fun javadocSectionDisplayName(sectionName: String?): String? = when(sectionName) { "param" -> "Parameters" - "throws", "exception" -> "Exceptions" + "throws", "exception" -> ContentTags.Exceptions else -> sectionName?.capitalize() } diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt index b125a7d4..eafa216b 100644 --- a/core/src/main/kotlin/javadoc/docbase.kt +++ b/core/src/main/kotlin/javadoc/docbase.kt @@ -309,10 +309,8 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio override fun thrownExceptions(): Array = emptyArray() // TODO override fun throwsTags(): Array = node.content.sections - .filter { it.tag == "Exceptions" } - .map { it.subjectName } - .filterNotNull() - .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it, NodeKind.Exception))) } + .filter { it.tag == ContentTags.Exceptions && it.subjectName != null } + .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) } .toTypedArray() override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO @@ -320,14 +318,7 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" } override fun paramTags(): Array = - (node.details(NodeKind.Parameter) - .filter(DocumentationNode::hasNonEmptyContent) - .map { ParamTagAdapter(module, this, it.name, false, it.content.children) } - - + node.content.sections - .filter { it.subjectName in parameters().map { it.name() } } - .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } - ).toTypedArray() + collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } }) override fun thrownExceptionTypes(): Array = emptyArray() override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) } @@ -342,14 +333,7 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio override fun typeParameters(): Array = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() override fun typeParamTags(): Array = - (node.details(NodeKind.TypeParameter) - .filter(DocumentationNode::hasNonEmptyContent) - .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } - - + node.content.sections - .filter { it.subjectName in typeParameters().map { it.simpleTypeName() } } - .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } - ).toTypedArray() + collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers -> when { @@ -443,14 +427,7 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod .toTypedArray() override fun typeParamTags(): Array = - (classNode.details(NodeKind.TypeParameter) - .filter(DocumentationNode::hasNonEmptyContent) - .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } - - + classNode.content.sections - .filter { it.subjectName in typeParameters().map { it.simpleTypeName() } } - .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) } - ).toTypedArray() + collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) override fun fields(): Array = fields(true) override fun fields(filter: Boolean): Array = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray() @@ -533,3 +510,14 @@ class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorR override fun specifiedClasses(): Array = classes() } + +private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) = + (node.details(kind) + .filter(DocumentationNode::hasNonEmptyContent) + .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } + + + node.content.sections + .filter(sectionFilter) + .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) }) + + .toTypedArray() \ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt index 5ee2cbac..9e023a81 100644 --- a/core/src/main/kotlin/javadoc/tags.kt +++ b/core/src/main/kotlin/javadoc/tags.kt @@ -114,20 +114,20 @@ class ParamTagAdapter(val module: ModuleNodeAdapter, } -class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter) : ThrowsTag { +class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List) : ThrowsTag { override fun name(): String = "@throws" override fun kind(): String = name() override fun holder(): Doc = holder override fun position(): SourcePosition? = holder.position() - override fun text(): String = "@throws ${type.qualifiedTypeName()}" - override fun inlineTags(): Array = emptyArray() + override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}" + override fun inlineTags(): Array = buildInlineTags(type.module, holder, content).toTypedArray() override fun firstSentenceTags(): Array = emptyArray() - override fun exceptionComment(): String = "" + override fun exceptionComment(): String = content.toString() override fun exceptionType(): Type = type override fun exception(): ClassDoc = type - override fun exceptionName(): String = type.qualifiedName() + override fun exceptionName(): String = type.qualifiedTypeName() } class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List) : Tag { diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt index f60c49fd..f7f86881 100644 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -128,6 +128,7 @@ class JavadocTest { val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0] assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text()) assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text()) + assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text()) } } diff --git a/core/testdata/javadoc/kdocKeywordsOnMethod.kt b/core/testdata/javadoc/kdocKeywordsOnMethod.kt index 63bbaba4..df5bbbe0 100644 --- a/core/testdata/javadoc/kdocKeywordsOnMethod.kt +++ b/core/testdata/javadoc/kdocKeywordsOnMethod.kt @@ -1,7 +1,12 @@ +class FireException : Exception + + /** * COMM * @param a Some string * @return value of a + * @throws FireException in case of fire */ +@Throws(FireException::class) fun my(a: String): String = a -- cgit