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 e2f446c216e..a5909c558a6 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 @@ -60,7 +60,7 @@ class OverridesCompletion( val classOrObject = position.getNonStrictParentOfType() ?: return - val members = OverrideMembersHandler().collectMembersToGenerate(classOrObject) + val members = OverrideMembersHandler(isConstructorParameter).collectMembersToGenerate(classOrObject) for (memberObject in members) { if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue @@ -114,7 +114,7 @@ class OverridesCompletion( // keep original modifiers val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text) - val prototype = memberObject.generateMember(context.project, isConstructorParameter) + val prototype = memberObject.generateMember(context.project) prototype.modifierList!!.replace(modifierList) val insertedMember = dummyMember.replaced(prototype) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/ImplementMembersHandler.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/ImplementMembersHandler.kt index 0df18f436e7..1d8c144b852 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/ImplementMembersHandler.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/ImplementMembersHandler.kt @@ -21,10 +21,15 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.resolve.OverrideResolver -public class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction { +public open class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction { override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection { return OverrideResolver.getMissingImplementations(descriptor) .map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) } @@ -39,3 +44,19 @@ public class ImplementMembersHandler : OverrideImplementMembersHandler(), Intent override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file) } + +public class ImplementAsConstructorParameter : ImplementMembersHandler() { + override fun getText() = "Implement as constructor parameters" + + override fun isValidForClass(classOrObject: KtClassOrObject): Boolean { + if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false + val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false + return OverrideResolver.getMissingImplementations(classDescriptor).any { it is PropertyDescriptor } + } + + override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection { + return OverrideResolver.getMissingImplementations(descriptor) + .filter { it is PropertyDescriptor } + .map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY, true) } + } +} \ 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 24093dc0864..2efb2489b0a 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 @@ -51,11 +51,13 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio protected abstract fun getChooserTitle(): String + protected open fun isValidForClass(classOrObject: KtClassOrObject) = true + override fun isValidFor(editor: Editor, file: PsiFile): Boolean { if (file !is KtFile) return false val elementAtCaret = file.findElementAt(editor.caretModel.offset) val classOrObject = elementAtCaret?.getNonStrictParentOfType() - return classOrObject != null + return classOrObject != null && isValidForClass(classOrObject) } protected abstract fun getNoMembersFoundHint(): String 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 94daccf6496..44d2d9b741a 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 @@ -39,15 +39,21 @@ interface OverrideMemberChooserObject : ClassMember { val descriptor: CallableMemberDescriptor val immediateSuper: CallableMemberDescriptor val bodyType: BodyType + val preferConstructorParameter: Boolean companion object { - fun create(project: Project, descriptor: CallableMemberDescriptor, immediateSuper: CallableMemberDescriptor, bodyType: BodyType): OverrideMemberChooserObject { + fun create(project: Project, + descriptor: CallableMemberDescriptor, + immediateSuper: CallableMemberDescriptor, + bodyType: BodyType, + preferConstructorParameter: Boolean = false + ): OverrideMemberChooserObject { val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) if (declaration != null) { - return WithDeclaration(descriptor, declaration, immediateSuper, bodyType) + return WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter) } else { - return WithoutDeclaration(descriptor, immediateSuper, bodyType) + return WithoutDeclaration(descriptor, immediateSuper, bodyType, preferConstructorParameter) } } @@ -55,7 +61,8 @@ interface OverrideMemberChooserObject : ClassMember { descriptor: CallableMemberDescriptor, declaration: PsiElement, override val immediateSuper: CallableMemberDescriptor, - override val bodyType: BodyType + override val bodyType: BodyType, + override val preferConstructorParameter: Boolean ) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject { override val descriptor: CallableMemberDescriptor @@ -65,7 +72,8 @@ interface OverrideMemberChooserObject : ClassMember { private class WithoutDeclaration( override val descriptor: CallableMemberDescriptor, override val immediateSuper: CallableMemberDescriptor, - override val bodyType: BodyType + override val bodyType: BodyType, + override val preferConstructorParameter: Boolean ) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject { override fun getParentNodeDelegate(): MemberChooserObject? { @@ -76,12 +84,9 @@ interface OverrideMemberChooserObject : ClassMember { } } -fun OverrideMemberChooserObject.generateMember(project: Project, asConstructorParameter: Boolean = false): KtCallableDeclaration { +fun OverrideMemberChooserObject.generateMember(project: Project): KtCallableDeclaration { val descriptor = immediateSuper - if (asConstructorParameter) { - assert(descriptor is PropertyDescriptor) { "asConstructorParameter is valid only for PropertyDescriptor" } - return generateConstructorParameter(project, descriptor as PropertyDescriptor) - } + if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor) return when (descriptor) { is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMembersHandler.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMembersHandler.kt index d128d22aaa1..fd308cf508e 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMembersHandler.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMembersHandler.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import java.util.* -public class OverrideMembersHandler : OverrideImplementMembersHandler() { +public class OverrideMembersHandler(private val preferConstructorParameters: Boolean = false) : OverrideImplementMembersHandler() { override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection { val result = ArrayList() for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) { @@ -73,7 +73,7 @@ public class OverrideMembersHandler : OverrideImplementMembersHandler() { else OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER - result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType)) + result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType, preferConstructorParameters)) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 269be530783..208f14a43e0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.intention.IntentionAction import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors.* +import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix @@ -156,10 +157,11 @@ public class QuickFixRegistrar : QuickFixContributor { USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS)) - val implementMethodsHandler = ImplementMembersHandler() - ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) - ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) - MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) + val implementMembersHandler = ImplementMembersHandler() + val implementMembersAsParametersHandler = ImplementAsConstructorParameter() + ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler) + ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler) + MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler) VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY) VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY) diff --git a/idea/testData/quickfix/override/implemenAsConstructorParameter.kt b/idea/testData/quickfix/override/implemenAsConstructorParameter.kt new file mode 100644 index 00000000000..33db889e037 --- /dev/null +++ b/idea/testData/quickfix/override/implemenAsConstructorParameter.kt @@ -0,0 +1,6 @@ +// "Implement as constructor parameters" "true" +interface I { + val foo: Int +} + +class A : I diff --git a/idea/testData/quickfix/override/implemenAsConstructorParameter.kt.after b/idea/testData/quickfix/override/implemenAsConstructorParameter.kt.after new file mode 100644 index 00000000000..02b1614b54e --- /dev/null +++ b/idea/testData/quickfix/override/implemenAsConstructorParameter.kt.after @@ -0,0 +1,6 @@ +// "Implement as constructor parameters" "true" +interface I { + val foo: Int +} + +class A(override val foo: Int) : I diff --git a/idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt b/idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt new file mode 100644 index 00000000000..f95f375039b --- /dev/null +++ b/idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt @@ -0,0 +1,13 @@ +// "Implement as constructor parameters" "false" +// ACTION: Create test +// ACTION: Implement members +// ACTION: Make 'A' abstract +// ACTION: Make internal +// ACTION: Make private +// ACTION: Move 'A' to separate file +// ERROR: Class 'A' must be declared abstract or implement abstract member public abstract fun foo(): kotlin.Int defined in I +interface I { + fun foo(): Int +} + +class A : I diff --git a/idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt b/idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt new file mode 100644 index 00000000000..88c72882fc6 --- /dev/null +++ b/idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt @@ -0,0 +1,12 @@ +// "Implement as constructor parameters" "false" +// ACTION: Create test +// ACTION: Implement members +// ACTION: Make internal +// ACTION: Make private +// ACTION: Move 'A' to separate file +// ERROR: Object 'A' must be declared abstract or implement abstract member public abstract val foo: kotlin.Int defined in I +interface I { + val foo: Int +} + +object A : I diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index f1d9a3a06fc..9a67301ffcf 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -5364,6 +5364,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("implemenAsConstructorParameter.kt") + public void testImplemenAsConstructorParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameter.kt"); + doTest(fileName); + } + + @TestMetadata("implemenAsConstructorParameterFunction.kt") + public void testImplemenAsConstructorParameterFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt"); + doTest(fileName); + } + + @TestMetadata("implemenAsConstructorParameterInObject.kt") + public void testImplemenAsConstructorParameterInObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt"); + doTest(fileName); + } + @TestMetadata("implementMember.kt") public void testImplementMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt");