Implement Members Quick-Fix: Support primary constructor parameters
#KT-8427 In Progress
This commit is contained in:
+2
-2
@@ -60,7 +60,7 @@ class OverridesCompletion(
|
|||||||
|
|
||||||
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return
|
||||||
|
|
||||||
val members = OverrideMembersHandler().collectMembersToGenerate(classOrObject)
|
val members = OverrideMembersHandler(isConstructorParameter).collectMembersToGenerate(classOrObject)
|
||||||
|
|
||||||
for (memberObject in members) {
|
for (memberObject in members) {
|
||||||
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
|
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
|
||||||
@@ -114,7 +114,7 @@ class OverridesCompletion(
|
|||||||
// keep original modifiers
|
// keep original modifiers
|
||||||
val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text)
|
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)
|
prototype.modifierList!!.replace(modifierList)
|
||||||
val insertedMember = dummyMember.replaced(prototype)
|
val insertedMember = dummyMember.replaced(prototype)
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -21,10 +21,15 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
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
|
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> {
|
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
|
||||||
return OverrideResolver.getMissingImplementations(descriptor)
|
return OverrideResolver.getMissingImplementations(descriptor)
|
||||||
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) }
|
.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)
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -51,11 +51,13 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
|
|||||||
|
|
||||||
protected abstract fun getChooserTitle(): String
|
protected abstract fun getChooserTitle(): String
|
||||||
|
|
||||||
|
protected open fun isValidForClass(classOrObject: KtClassOrObject) = true
|
||||||
|
|
||||||
override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
|
override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
|
||||||
if (file !is KtFile) return false
|
if (file !is KtFile) return false
|
||||||
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
|
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
|
||||||
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
|
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
|
||||||
return classOrObject != null
|
return classOrObject != null && isValidForClass(classOrObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun getNoMembersFoundHint(): String
|
protected abstract fun getNoMembersFoundHint(): String
|
||||||
|
|||||||
+15
-10
@@ -39,15 +39,21 @@ interface OverrideMemberChooserObject : ClassMember {
|
|||||||
val descriptor: CallableMemberDescriptor
|
val descriptor: CallableMemberDescriptor
|
||||||
val immediateSuper: CallableMemberDescriptor
|
val immediateSuper: CallableMemberDescriptor
|
||||||
val bodyType: BodyType
|
val bodyType: BodyType
|
||||||
|
val preferConstructorParameter: Boolean
|
||||||
|
|
||||||
companion object {
|
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)
|
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
|
||||||
if (declaration != null) {
|
if (declaration != null) {
|
||||||
return WithDeclaration(descriptor, declaration, immediateSuper, bodyType)
|
return WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return WithoutDeclaration(descriptor, immediateSuper, bodyType)
|
return WithoutDeclaration(descriptor, immediateSuper, bodyType, preferConstructorParameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +61,8 @@ interface OverrideMemberChooserObject : ClassMember {
|
|||||||
descriptor: CallableMemberDescriptor,
|
descriptor: CallableMemberDescriptor,
|
||||||
declaration: PsiElement,
|
declaration: PsiElement,
|
||||||
override val immediateSuper: CallableMemberDescriptor,
|
override val immediateSuper: CallableMemberDescriptor,
|
||||||
override val bodyType: BodyType
|
override val bodyType: BodyType,
|
||||||
|
override val preferConstructorParameter: Boolean
|
||||||
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
|
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
|
||||||
|
|
||||||
override val descriptor: CallableMemberDescriptor
|
override val descriptor: CallableMemberDescriptor
|
||||||
@@ -65,7 +72,8 @@ interface OverrideMemberChooserObject : ClassMember {
|
|||||||
private class WithoutDeclaration(
|
private class WithoutDeclaration(
|
||||||
override val descriptor: CallableMemberDescriptor,
|
override val descriptor: CallableMemberDescriptor,
|
||||||
override val immediateSuper: 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 {
|
) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject {
|
||||||
|
|
||||||
override fun getParentNodeDelegate(): MemberChooserObject? {
|
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
|
val descriptor = immediateSuper
|
||||||
if (asConstructorParameter) {
|
if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor)
|
||||||
assert(descriptor is PropertyDescriptor) { "asConstructorParameter is valid only for PropertyDescriptor" }
|
|
||||||
return generateConstructorParameter(project, descriptor as PropertyDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (descriptor) {
|
return when (descriptor) {
|
||||||
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)
|
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import java.util.*
|
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> {
|
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
|
||||||
val result = ArrayList<OverrideMemberChooserObject>()
|
val result = ArrayList<OverrideMemberChooserObject>()
|
||||||
for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) {
|
for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) {
|
||||||
@@ -73,7 +73,7 @@ public class OverrideMembersHandler : OverrideImplementMembersHandler() {
|
|||||||
else
|
else
|
||||||
OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER
|
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 com.intellij.codeInsight.intention.IntentionAction
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
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.core.overrideImplement.ImplementMembersHandler
|
||||||
import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
|
import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
|
||||||
import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix
|
import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix
|
||||||
@@ -156,10 +157,11 @@ public class QuickFixRegistrar : QuickFixContributor {
|
|||||||
USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS))
|
USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS))
|
||||||
|
|
||||||
|
|
||||||
val implementMethodsHandler = ImplementMembersHandler()
|
val implementMembersHandler = ImplementMembersHandler()
|
||||||
ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
|
val implementMembersAsParametersHandler = ImplementAsConstructorParameter()
|
||||||
ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
|
ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
|
||||||
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
|
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_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
|
||||||
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_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);
|
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")
|
@TestMetadata("implementMember.kt")
|
||||||
public void testImplementMember() throws Exception {
|
public void testImplementMember() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user