From 94c241443b63c48e263a3837f171eb4ac12001cc Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 28 Apr 2016 17:02:43 +0200 Subject: [PATCH] support @receiver KDoc tag; somewhat more precise tag name completion --- .../kotlin/kdoc/parser/KDocKnownTag.kt | 1 + .../completion/KDocCompletionContributor.kt | 17 ++++++++++++++++- idea/idea-completion/testData/kdoc/TagName.kt | 1 + .../testData/kdoc/TagNameInClass.kt | 12 ++++++++++++ .../kdoc/TagNameInExtensionFunction.kt | 12 ++++++++++++ .../test/KDocCompletionTestGenerated.java | 12 ++++++++++++ .../kotlin/idea/kdoc/KDocRenderer.kt | 1 + .../quickDoc/OnMethodUsageWithReceiver.kt | 19 +++++++++++++++++++ .../QuickDocProviderTestGenerated.java | 6 ++++++ 9 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 idea/idea-completion/testData/kdoc/TagNameInClass.kt create mode 100644 idea/idea-completion/testData/kdoc/TagNameInExtensionFunction.kt create mode 100644 idea/testData/editor/quickDoc/OnMethodUsageWithReceiver.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocKnownTag.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocKnownTag.kt index 01c70512b46..137af4a022b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocKnownTag.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocKnownTag.kt @@ -23,6 +23,7 @@ enum class KDocKnownTag private constructor(val isReferenceRequired: Boolean, va THROWS(true, false), EXCEPTION(true, false), PARAM(true, false), + RECEIVER(false, false), RETURN(false, false), SEE(true, false), SINCE(false, false), diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt index 936e719cd75..c357167cce3 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt @@ -32,8 +32,13 @@ import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink import org.jetbrains.kotlin.kdoc.psi.impl.KDocName +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext @@ -134,9 +139,19 @@ object KDocTagCompletionProvider: CompletionProvider() { if (prefix.length > 0 && !prefix.startsWith('@')) { return } + val kdocOwner = parameters.position.getNonStrictParentOfType()?.getOwner() val resultWithPrefix = result.withPrefixMatcher(prefix) KDocKnownTag.values().forEach { - resultWithPrefix.addElement(LookupElementBuilder.create("@" + it.name.toLowerCase())) + if (kdocOwner == null || it.isApplicable(kdocOwner)) { + resultWithPrefix.addElement(LookupElementBuilder.create("@" + it.name.toLowerCase())) + } } } + + private fun KDocKnownTag.isApplicable(declaration: KtDeclaration) = when(this) { + KDocKnownTag.CONSTRUCTOR, KDocKnownTag.PROPERTY -> declaration is KtClassOrObject + KDocKnownTag.RETURN -> declaration is KtNamedFunction + KDocKnownTag.RECEIVER -> declaration is KtNamedFunction && declaration.receiverTypeReference != null + else -> true + } } diff --git a/idea/idea-completion/testData/kdoc/TagName.kt b/idea/idea-completion/testData/kdoc/TagName.kt index 5d8bd23d1f0..2445b4f9752 100644 --- a/idea/idea-completion/testData/kdoc/TagName.kt +++ b/idea/idea-completion/testData/kdoc/TagName.kt @@ -7,3 +7,4 @@ fun f(x: Int): Int { // EXIST: @param // EXIST: @return // EXIST: @suppress +// ABSENT: @receiver diff --git a/idea/idea-completion/testData/kdoc/TagNameInClass.kt b/idea/idea-completion/testData/kdoc/TagNameInClass.kt new file mode 100644 index 00000000000..7a9f9d92ec0 --- /dev/null +++ b/idea/idea-completion/testData/kdoc/TagNameInClass.kt @@ -0,0 +1,12 @@ +/** + * + */ +class C { +} + +// EXIST: @param +// EXIST: @suppress +// EXIST: @constructor +// EXIST: @property +// ABSENT: @return +// ABSENT: @receiver diff --git a/idea/idea-completion/testData/kdoc/TagNameInExtensionFunction.kt b/idea/idea-completion/testData/kdoc/TagNameInExtensionFunction.kt new file mode 100644 index 00000000000..e31c5449882 --- /dev/null +++ b/idea/idea-completion/testData/kdoc/TagNameInExtensionFunction.kt @@ -0,0 +1,12 @@ +/** + * + */ +fun Int.f(): Int { +} + +// EXIST: @param +// EXIST: @return +// EXIST: @suppress +// EXIST: @receiver +// ABSENT: @constructor +// ABSENT: @property diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java index 322ab068a07..76c51ac191e 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KDocCompletionTestGenerated.java @@ -77,6 +77,18 @@ public class KDocCompletionTestGenerated extends AbstractJvmBasicCompletionTest doTest(fileName); } + @TestMetadata("TagNameInClass.kt") + public void testTagNameInClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/TagNameInClass.kt"); + doTest(fileName); + } + + @TestMetadata("TagNameInExtensionFunction.kt") + public void testTagNameInExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/TagNameInExtensionFunction.kt"); + doTest(fileName); + } + @TestMetadata("TagNameMiddle.kt") public void testTagNameMiddle() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/kdoc/TagNameMiddle.kt"); diff --git a/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt index 3a8fdc0b372..fb3cbcea808 100644 --- a/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt @@ -33,6 +33,7 @@ object KDocRenderer { result.append(markdownToHtml(content, allowSingleParagraph = true)) if (docComment is KDocSection) { result.append("\n") + renderTag(docComment.findTagByName("receiver"), "Receiver", result) val paramTags = docComment.findTagsByName("param").filter { it.getSubjectName() != null } renderTagList(paramTags, "Parameters", result) diff --git a/idea/testData/editor/quickDoc/OnMethodUsageWithReceiver.kt b/idea/testData/editor/quickDoc/OnMethodUsageWithReceiver.kt new file mode 100644 index 00000000000..6caef876148 --- /dev/null +++ b/idea/testData/editor/quickDoc/OnMethodUsageWithReceiver.kt @@ -0,0 +1,19 @@ +/** +Some documentation + + * @receiver Some int + * @param b String + * @return Return [a] and nothing else + */ +fun Int.testMethod(b: String) { + +} + +fun test() { + 1.testMethod("value") +} + +//INFO: public fun Int.testMethod(b: String): Unit defined in root package

Some documentation

+//INFO:
Receiver:
Some int
+//INFO:
Parameters:
b - String
+//INFO:
Returns:
Return a and nothing else
diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java index 139e5581d3d..fdfbe7277f4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocProviderTestGenerated.java @@ -143,6 +143,12 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest doTest(fileName); } + @TestMetadata("OnMethodUsageWithReceiver.kt") + public void testOnMethodUsageWithReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnMethodUsageWithReceiver.kt"); + doTest(fileName); + } + @TestMetadata("OnMethodUsageWithReturnAndLink.kt") public void testOnMethodUsageWithReturnAndLink() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndLink.kt");