Implement Members Quick-Fix: Support primary constructor parameters

#KT-8427 In Progress
This commit is contained in:
Alexey Sedunov
2015-12-24 16:40:24 +03:00
committed by Alexey
parent 00cc9d4150
commit 2b4f03feef
11 changed files with 105 additions and 20 deletions
@@ -60,7 +60,7 @@ class OverridesCompletion(
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: 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)
@@ -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<OverrideMemberChooserObject> {
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<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.filter { it is PropertyDescriptor }
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY, true) }
}
}
@@ -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<KtClassOrObject>()
return classOrObject != null
return classOrObject != null && isValidForClass(classOrObject)
}
protected abstract fun getNoMembersFoundHint(): String
@@ -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)
@@ -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<OverrideMemberChooserObject> {
val result = ArrayList<OverrideMemberChooserObject>()
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))
}
}
}
@@ -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)
@@ -0,0 +1,6 @@
// "Implement as constructor parameters" "true"
interface I {
val foo: Int
}
<caret>class A : I
@@ -0,0 +1,6 @@
// "Implement as constructor parameters" "true"
interface I {
val foo: Int
}
<caret>class A(override val foo: Int) : I
@@ -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
}
<caret>class A : I
@@ -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
}
<caret>object A : I
@@ -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");