diff --git a/ChangeLog.md b/ChangeLog.md index 0cf5a1b24df..4f6381e64bf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -349,11 +349,13 @@ - [`KT-11704`](https://youtrack.jetbrains.com/issue/KT-11704) Support file path references inside of Kotlin string literals - [`KT-12076`](https://youtrack.jetbrains.com/issue/KT-12076) Kotlin Plugin update check: alwats display installed version number - [`KT-11814`](https://youtrack.jetbrains.com/issue/KT-11814) New icon for kotlin annotation classes +- [`KT-12735`](https://youtrack.jetbrains.com/issue/KT-12735) Convert JavaDoc to KDoc when overriding Java class member in Kotlin ###### Issues fixed - [`KT-5960`](https://youtrack.jetbrains.com/issue/KT-5960) Can't find usages for Java methods used from Kotlin by call convention - [`KT-8362`](https://youtrack.jetbrains.com/issue/KT-8362) "New Kotlin file": Keywords should be escaped in package name +- [`KT-8682`](https://youtrack.jetbrains.com/issue/KT-8682) Respect "Copy JavaDoc" option in the "Override/Implement Members..." dialog - [`KT-8817`](https://youtrack.jetbrains.com/issue/KT-8817) Fixed rename of Java getters/setters through synthetic property references in Kotlin - [`KT-9399`](https://youtrack.jetbrains.com/issue/KT-9399) Find Usages omits Kotlin annotation parameter usage in Java source - [`KT-9797`](https://youtrack.jetbrains.com/issue/KT-9797) "Kotlin Bytecode" toolwindow breaks after closing diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt index 97a90b05137..2aeb0a82a6d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt @@ -114,7 +114,7 @@ class OverridesCompletion( // keep original modifiers val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text) - val prototype = memberObject.generateMember(context.project) + val prototype = memberObject.generateMember(context.project, false) prototype.modifierList!!.replace(modifierList) val insertedMember = dummyMember.replaced(prototype) diff --git a/idea/idea-core/idea-core.iml b/idea/idea-core/idea-core.iml index 3238fb2a99b..a9a10941722 100644 --- a/idea/idea-core/idea-core.iml +++ b/idea/idea-core/idea-core.iml @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt index de384c198b5..5bab68cc526 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt @@ -72,18 +72,22 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle return } - val selectedElements = if (implementAll) { - members + val copyDoc: Boolean + val selectedElements: Collection + if (implementAll) { + selectedElements = members + copyDoc = false } else { val chooser = showOverrideImplementChooser(project, members.toTypedArray()) ?: return - chooser.selectedElements ?: return + selectedElements = chooser.selectedElements ?: return + copyDoc = chooser.isCopyJavadoc } if (selectedElements.isEmpty()) return PsiDocumentManager.getInstance(project).commitAllDocuments() - generateMembers(editor, classOrObject, selectedElements) + generateMembers(editor, classOrObject, selectedElements, copyDoc) } override fun invoke(project: Project, editor: Editor, file: PsiFile) { @@ -93,9 +97,14 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle override fun startInWriteAction(): Boolean = false companion object { - fun generateMembers(editor: Editor?, classOrObject: KtClassOrObject, selectedElements: Collection) { + fun generateMembers( + editor: Editor?, + classOrObject: KtClassOrObject, + selectedElements: Collection, + copyDoc: Boolean + ) { val project = classOrObject.project - insertMembersAfter(editor, classOrObject, selectedElements.map { it.generateMember(project) }) + insertMembersAfter(editor, classOrObject, selectedElements.map { it.generateMember(project, copyDoc) }) } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt index c7b9cc02886..860a6e8c9a3 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt @@ -20,6 +20,7 @@ import com.intellij.codeInsight.generation.ClassMember import com.intellij.codeInsight.generation.MemberChooserObject import com.intellij.codeInsight.generation.MemberChooserObjectBase import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocCommentOwner import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* @@ -27,8 +28,11 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.TemplateKind import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject +import org.jetbrains.kotlin.idea.j2k.IdeaDocCommentConverter +import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.findDocComment.findDocComment import org.jetbrains.kotlin.renderer.* import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden @@ -88,15 +92,33 @@ interface OverrideMemberChooserObject : ClassMember { } } -fun OverrideMemberChooserObject.generateMember(project: Project): KtCallableDeclaration { +fun OverrideMemberChooserObject.generateMember(project: Project, copyDoc: Boolean): KtCallableDeclaration { val descriptor = immediateSuper if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor) - return when (descriptor) { + val newMember: KtCallableDeclaration = when (descriptor) { is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType) is PropertyDescriptor -> generateProperty(project, descriptor, bodyType) else -> error("Unknown member to override: $descriptor") } + + if (copyDoc) { + val superDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) + val kDoc = when (superDeclaration) { + is KtDeclaration -> + findDocComment(superDeclaration) + is PsiDocCommentOwner -> { + val kDocText = superDeclaration.docComment?.let { IdeaDocCommentConverter.convertDocComment(it) } + if (kDocText.isNullOrEmpty()) null else KDocElementFactory(project).createKDocFromText(kDocText!!) + } + else -> null + } + if (kDoc != null) { + newMember.addAfter(kDoc, null) + } + } + + return newMember } private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions { diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/IdeaDocCommentConverter.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/j2k/IdeaDocCommentConverter.kt similarity index 100% rename from idea/src/org/jetbrains/kotlin/idea/j2k/IdeaDocCommentConverter.kt rename to idea/idea-core/src/org/jetbrains/kotlin/idea/j2k/IdeaDocCommentConverter.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt index ddd788e7f81..49a58226e23 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/utils.kt @@ -78,5 +78,5 @@ fun confirmMemberRewrite(targetClass: KtClass, vararg descriptors: FunctionDescr fun generateFunctionSkeleton(descriptor: FunctionDescriptor, project: Project): KtNamedFunction { return OverrideMemberChooserObject .create(project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY) - .generateMember(project) as KtNamedFunction + .generateMember(project, false) as KtNamedFunction } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt index 18cf507b9ed..34fb2536d70 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt @@ -138,7 +138,7 @@ abstract class ImplementAbstractMemberIntentionBase : descriptorToImplement, OverrideMemberChooserObject.BodyType.EMPTY, preferConstructorParameters) - OverrideImplementMembersHandler.generateMembers(null, targetClass, chooserObject.singletonList()) + OverrideImplementMembersHandler.generateMembers(null, targetClass, chooserObject.singletonList(), false) } private fun implementInJavaClass(member: KtNamedDeclaration, targetClass: PsiClass) { diff --git a/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/A.java b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/A.java new file mode 100644 index 00000000000..70a8b8f1eb7 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/A.java @@ -0,0 +1,10 @@ +package foo; + +public class A { + /** + * @return TEST + */ + public int foo() { + return 1; + } +} diff --git a/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt new file mode 100644 index 00000000000..2af8ba23706 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt @@ -0,0 +1,7 @@ +// COPY_DOC + +import foo.A + +class B : A() { + +} diff --git a/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt.after b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt.after new file mode 100644 index 00000000000..0bf06f98b56 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/convertJavaDoc/foo/Impl.kt.after @@ -0,0 +1,12 @@ +// COPY_DOC + +import foo.A + +class B : A() { + /** + * @return TEST + */ + override fun foo(): Int { + return super.foo() + } +} diff --git a/idea/testData/codeInsight/overrideImplement/copyKDoc.kt b/idea/testData/codeInsight/overrideImplement/copyKDoc.kt new file mode 100644 index 00000000000..4e33c92f8fd --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/copyKDoc.kt @@ -0,0 +1,11 @@ +// COPY_DOC +abstract class A { + /** + * @see TEST + */ + abstract fun foo() +} + +class B : A() { + +} \ No newline at end of file diff --git a/idea/testData/codeInsight/overrideImplement/copyKDoc.kt.after b/idea/testData/codeInsight/overrideImplement/copyKDoc.kt.after new file mode 100644 index 00000000000..c051dd27ed4 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/copyKDoc.kt.after @@ -0,0 +1,16 @@ +// COPY_DOC +abstract class A { + /** + * @see TEST + */ + abstract fun foo() +} + +class B : A() { + /** + * @see TEST + */ + override fun foo() { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.kt index 028ebac06f2..7377d12808b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.test.dumpTextWithErrors import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.TagsTestDataUtil import org.jetbrains.kotlin.utils.rethrow @@ -171,8 +172,9 @@ abstract class AbstractOverrideImplementTest : KotlinLightCodeInsightFixtureTest classOrObject: KtClassOrObject, selectedElements: List) { try { + val copyDoc = InTextDirectivesUtils.isDirectiveDefined(classOrObject.containingFile.text, "// COPY_DOC") myFixture.project.executeWriteCommand("") { - OverrideImplementMembersHandler.generateMembers(myFixture.editor, classOrObject, selectedElements) + OverrideImplementMembersHandler.generateMembers(myFixture.editor, classOrObject, selectedElements, copyDoc) } } catch (throwable: Throwable) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.kt index 69e950cd0b4..14f16852e59 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.kt @@ -235,4 +235,12 @@ class OverrideImplementTest : AbstractOverrideImplementTest() { fun testEqualsInInterface() { doOverrideFileTest("equals") } + + fun testCopyKDoc() { + doOverrideFileTest("foo") + } + + fun testConvertJavaDoc() { + doOverrideDirectoryTest("foo") + } }