KT-8542 Override dialog: only one class from hierarchy is shown
#KT-8542 Fixed
This commit is contained in:
Generated
+1
@@ -5,6 +5,7 @@
|
||||
<w>decapitalize</w>
|
||||
<w>delegator</w>
|
||||
<w>funs</w>
|
||||
<w>immediates</w>
|
||||
<w>initializers</w>
|
||||
<w>inserter</w>
|
||||
<w>negatable</w>
|
||||
|
||||
+12
-4
@@ -18,16 +18,18 @@ package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import com.intellij.codeInsight.generation.ClassMemberWithElement
|
||||
import com.intellij.codeInsight.generation.MemberChooserObject
|
||||
import com.intellij.codeInsight.generation.MemberChooserObjectBase
|
||||
import com.intellij.codeInsight.generation.PsiElementMemberChooserObject
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
@@ -39,10 +41,16 @@ import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import javax.swing.Icon
|
||||
|
||||
public class DescriptorClassMember(
|
||||
private val psiElement: PsiElement,
|
||||
class OverrideMemberChooserObject(
|
||||
project: Project,
|
||||
val member: CallableMemberDescriptor,
|
||||
val immediateSuper: CallableMemberDescriptor
|
||||
) : DescriptorClassMember(DescriptorToSourceUtilsIde.getAnyDeclaration(project, member)!!, member)
|
||||
|
||||
public open class DescriptorClassMember(
|
||||
psiElement: PsiElement,
|
||||
public val descriptor: DeclarationDescriptor
|
||||
) : MemberChooserObjectBase(DescriptorClassMember.getText(descriptor), DescriptorClassMember.getIcon(psiElement, descriptor)), ClassMemberWithElement {
|
||||
) : PsiElementMemberChooserObject(psiElement, DescriptorClassMember.getText(descriptor), DescriptorClassMember.getIcon(psiElement, descriptor)), ClassMemberWithElement {
|
||||
|
||||
override fun getParentNodeDelegate(): MemberChooserObject {
|
||||
val parent = descriptor.containingDeclaration ?: error("No parent for $descriptor")
|
||||
|
||||
+4
-1
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.resolve.OverrideResolver
|
||||
|
||||
public class ImplementMethodsHandler : OverrideImplementMethodsHandler(), IntentionAction {
|
||||
override fun collectMethodsToGenerate(descriptor: ClassDescriptor) = OverrideResolver.getMissingImplementations(descriptor)
|
||||
override fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
|
||||
return OverrideResolver.getMissingImplementations(descriptor)
|
||||
.map { OverrideMemberChooserObject(project, it, it) }
|
||||
}
|
||||
|
||||
override fun getChooserTitle() = "Implement Members"
|
||||
|
||||
|
||||
+14
-35
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
import com.intellij.codeInsight.hint.HintManager
|
||||
import com.intellij.ide.util.MemberChooser
|
||||
import com.intellij.lang.LanguageCodeInsightActionHandler
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
@@ -29,7 +28,6 @@ import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.quickfix.moveCaretIntoGeneratedElement
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
@@ -41,14 +39,14 @@ import java.util.ArrayList
|
||||
|
||||
public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActionHandler {
|
||||
|
||||
public fun collectMethodsToGenerate(classOrObject: JetClassOrObject): Set<CallableMemberDescriptor> {
|
||||
public fun collectMethodsToGenerate(classOrObject: JetClassOrObject): Collection<OverrideMemberChooserObject> {
|
||||
val descriptor = classOrObject.resolveToDescriptor() as? ClassDescriptor ?: return emptySet()
|
||||
return collectMethodsToGenerate(descriptor)
|
||||
return collectMethodsToGenerate(descriptor, classOrObject.project)
|
||||
}
|
||||
|
||||
protected abstract fun collectMethodsToGenerate(descriptor: ClassDescriptor): Set<CallableMemberDescriptor>
|
||||
protected abstract fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject>
|
||||
|
||||
private fun showOverrideImplementChooser(project: Project, members: Array<DescriptorClassMember>): MemberChooser<DescriptorClassMember>? {
|
||||
private fun showOverrideImplementChooser(project: Project, members: Array<OverrideMemberChooserObject>): MemberChooser<OverrideMemberChooserObject>? {
|
||||
val chooser = MemberChooser(members, true, true, project)
|
||||
chooser.title = getChooserTitle()
|
||||
chooser.show()
|
||||
@@ -71,15 +69,13 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
|
||||
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
|
||||
val classOrObject = elementAtCaret?.getNonStrictParentOfType<JetClassOrObject>()!!
|
||||
|
||||
val missingImplementations = collectMethodsToGenerate(classOrObject)
|
||||
if (missingImplementations.isEmpty() && !implementAll) {
|
||||
val members = collectMethodsToGenerate(classOrObject)
|
||||
if (members.isEmpty() && !implementAll) {
|
||||
HintManager.getInstance().showErrorHint(editor, getNoMethodsFoundHint())
|
||||
return
|
||||
}
|
||||
|
||||
val members = membersFromDescriptors(file as JetFile, missingImplementations)
|
||||
|
||||
val selectedElements: List<DescriptorClassMember> = if (implementAll) {
|
||||
val selectedElements = if (implementAll) {
|
||||
members
|
||||
}
|
||||
else {
|
||||
@@ -108,23 +104,7 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
|
||||
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(javaClass<OverrideImplementMethodsHandler>().canonicalName)
|
||||
|
||||
public fun membersFromDescriptors(file: JetFile, missingImplementations: Iterable<CallableMemberDescriptor>): List<DescriptorClassMember> {
|
||||
val members = ArrayList<DescriptorClassMember>()
|
||||
for (memberDescriptor in missingImplementations) {
|
||||
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, memberDescriptor)
|
||||
if (declaration == null) {
|
||||
LOG.error("Can not find declaration for descriptor $memberDescriptor")
|
||||
}
|
||||
else {
|
||||
members.add(DescriptorClassMember(declaration, memberDescriptor))
|
||||
}
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: List<DescriptorClassMember>) {
|
||||
public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
|
||||
runWriteAction {
|
||||
val body = classOrObject.getOrCreateBody()
|
||||
|
||||
@@ -203,16 +183,15 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
|
||||
return whiteSpace
|
||||
}
|
||||
|
||||
private fun generateOverridingMembers(selectedElements: List<DescriptorClassMember>,
|
||||
private fun generateOverridingMembers(selectedElements: Collection<OverrideMemberChooserObject>,
|
||||
classOrObject: JetClassOrObject): List<JetElement> {
|
||||
val overridingMembers = ArrayList<JetElement>()
|
||||
for (selectedElement in selectedElements) {
|
||||
val descriptor = selectedElement.descriptor
|
||||
if (descriptor is SimpleFunctionDescriptor) {
|
||||
overridingMembers.add(overrideFunction(classOrObject, descriptor))
|
||||
}
|
||||
else if (descriptor is PropertyDescriptor) {
|
||||
overridingMembers.add(overrideProperty(classOrObject, descriptor))
|
||||
val descriptor = selectedElement.immediateSuper
|
||||
when (descriptor) {
|
||||
is SimpleFunctionDescriptor -> overridingMembers.add(overrideFunction(classOrObject, descriptor))
|
||||
is PropertyDescriptor -> overridingMembers.add(overrideProperty(classOrObject, descriptor))
|
||||
else -> error("Unknown member to override: $descriptor")
|
||||
}
|
||||
}
|
||||
return overridingMembers
|
||||
|
||||
+45
-37
@@ -16,53 +16,61 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import com.google.common.collect.LinkedHashMultimap
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.resolve.OverrideResolver
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashMap
|
||||
|
||||
public class OverrideMethodsHandler : OverrideImplementMethodsHandler() {
|
||||
override fun collectMethodsToGenerate(descriptor: ClassDescriptor): Set<CallableMemberDescriptor> {
|
||||
val superMethods = collectSuperMethods(descriptor)
|
||||
for (member in descriptor.defaultType.memberScope.getAllDescriptors()) {
|
||||
if (member is CallableMemberDescriptor) {
|
||||
if (member.kind == CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
superMethods.removeAll(member.overriddenDescriptors)
|
||||
override fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
|
||||
val result = ArrayList<OverrideMemberChooserObject>()
|
||||
for (member in descriptor.unsubstitutedMemberScope.getAllDescriptors()) {
|
||||
if (member is CallableMemberDescriptor && !member.isReal()) {
|
||||
val overridden = member.overriddenDescriptors
|
||||
if (overridden.any { it.modality == Modality.FINAL }) continue
|
||||
|
||||
val realSuperToImmediates = LinkedHashMap<CallableMemberDescriptor, MutableCollection<CallableMemberDescriptor>>()
|
||||
for (immediateSuper in overridden) {
|
||||
for (realSuper in toRealSupers(immediateSuper)) {
|
||||
realSuperToImmediates.getOrPut(realSuper) { ArrayList(1) }.add(immediateSuper)
|
||||
}
|
||||
}
|
||||
|
||||
val realSupers = realSuperToImmediates.keySet()
|
||||
val nonAbstractRealSupers = realSupers.filter { it.modality != Modality.ABSTRACT }
|
||||
val realSupersToUse = if (nonAbstractRealSupers.isNotEmpty()) {
|
||||
nonAbstractRealSupers
|
||||
}
|
||||
else {
|
||||
listOf(realSupers.first())
|
||||
}
|
||||
|
||||
for (realSuper in realSupersToUse) {
|
||||
val immediateSupers = realSuperToImmediates[realSuper]!!
|
||||
assert(immediateSupers.isNotEmpty())
|
||||
|
||||
val immediateSuperToUse = if (immediateSupers.size() == 1) {
|
||||
immediateSupers.single()
|
||||
}
|
||||
else {
|
||||
immediateSupers.singleOrNull { (it.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.CLASS } ?: immediateSupers.first()
|
||||
}
|
||||
result.add(OverrideMemberChooserObject(project, realSuper, immediateSuperToUse))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return superMethods
|
||||
.filter { it.modality.isOverridable && !isOrOverridesSynthesized(it) }
|
||||
.toSet()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun collectSuperMethods(classDescriptor: ClassDescriptor): MutableSet<CallableMemberDescriptor> {
|
||||
val inheritedFunctionsSet = classDescriptor.typeConstructor.supertypes
|
||||
.flatMap { it.memberScope.getAllDescriptors() }
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
.toSet()
|
||||
private fun CallableMemberDescriptor.isReal() = kind == CallableMemberDescriptor.Kind.DECLARATION
|
||||
|
||||
// Only those actually inherited
|
||||
val filteredMembers = OverrideResolver.filterOutOverridden(inheritedFunctionsSet)
|
||||
|
||||
// Group members with "the same" signature
|
||||
val factoredMembers = LinkedHashMultimap.create<CallableMemberDescriptor, CallableMemberDescriptor>()
|
||||
for (one in filteredMembers) {
|
||||
if (factoredMembers.values().contains(one)) continue
|
||||
for (another in filteredMembers) {
|
||||
// if (one == another) continue;
|
||||
factoredMembers.put(one, one)
|
||||
if (OverridingUtil.DEFAULT.isOverridableBy(one, another).result == OVERRIDABLE || OverridingUtil.DEFAULT.isOverridableBy(another, one).result == OVERRIDABLE) {
|
||||
factoredMembers.put(one, another)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return factoredMembers.keySet()
|
||||
private fun toRealSupers(immediateSuper: CallableMemberDescriptor): Collection<CallableMemberDescriptor> {
|
||||
val overridden = immediateSuper.overriddenDescriptors
|
||||
if (overridden.isEmpty()) return listOf(immediateSuper)
|
||||
return overridden.flatMap { toRealSupers(it) }.toSet()
|
||||
}
|
||||
|
||||
override fun getChooserTitle() = "Override Members"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
interface I {
|
||||
open fun foo(){}
|
||||
}
|
||||
|
||||
open class A {
|
||||
open fun foo(){}
|
||||
}
|
||||
|
||||
class C : A(), I {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
interface I {
|
||||
open fun foo(){}
|
||||
}
|
||||
|
||||
open class A {
|
||||
open fun foo(){}
|
||||
}
|
||||
|
||||
class C : A(), I {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<selection><caret>return super<A>.equals(other)</selection>
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
super<A>.foo()
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
super<I>.foo()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return super<A>.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super<A>.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
open class A {
|
||||
fun a(){}
|
||||
fun b(){}
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun b()
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
open fun f(){}
|
||||
abstract fun g()
|
||||
fun h(){}
|
||||
}
|
||||
|
||||
class C : B(), I {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
open class A {
|
||||
fun a(){}
|
||||
fun b(){}
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun b()
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
open fun f(){}
|
||||
abstract fun g()
|
||||
fun h(){}
|
||||
}
|
||||
|
||||
class C : B(), I {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<selection><caret>return super<B>.equals(other)</selection>
|
||||
}
|
||||
|
||||
override fun f() {
|
||||
super<B>.f()
|
||||
}
|
||||
|
||||
override fun g() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return super<B>.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super<B>.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
abstract class A : I1 {
|
||||
open fun a(){}
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
fun i1()
|
||||
fun i()
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
fun i2()
|
||||
fun a()
|
||||
}
|
||||
|
||||
interface I3 {
|
||||
fun i()
|
||||
}
|
||||
|
||||
abstract class B : I2, A(), I3 {
|
||||
<caret>
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
abstract class A : I1 {
|
||||
open fun a(){}
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
fun i1()
|
||||
fun i()
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
fun i2()
|
||||
fun a()
|
||||
}
|
||||
|
||||
interface I3 {
|
||||
fun i()
|
||||
}
|
||||
|
||||
abstract class B : I2, A(), I3 {
|
||||
override fun a() {
|
||||
<selection><caret>super<A>.a()</selection>
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return super<A>.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return super<A>.hashCode()
|
||||
}
|
||||
|
||||
override fun i() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun i1() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun i2() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super<A>.toString()
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight;
|
||||
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil;
|
||||
import com.intellij.codeInsight.generation.PsiMethodMember;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.util.SmartList;
|
||||
import kotlin.KotlinPackage;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.DescriptorClassMember;
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMethodsHandler;
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMethodsHandler;
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMethodsHandler;
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor;
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.resolve.OverrideResolver;
|
||||
import org.jetbrains.kotlin.test.JetTestUtils;
|
||||
import org.jetbrains.kotlin.test.TagsTestDataUtil;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractOverrideImplementTest extends JetLightCodeInsightFixtureTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JetLightProjectDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
protected void doImplementFileTest() {
|
||||
doFileTest(new ImplementMethodsHandler());
|
||||
}
|
||||
|
||||
protected void doOverrideFileTest() {
|
||||
doFileTest(new OverrideMethodsHandler());
|
||||
}
|
||||
|
||||
protected void doMultiImplementFileTest() {
|
||||
doMultiFileTest(new ImplementMethodsHandler());
|
||||
}
|
||||
|
||||
protected void doMultiOverrideFileTest() {
|
||||
doMultiFileTest(new OverrideMethodsHandler());
|
||||
}
|
||||
|
||||
protected void doImplementDirectoryTest() {
|
||||
doDirectoryTest(new ImplementMethodsHandler());
|
||||
}
|
||||
|
||||
protected void doOverrideDirectoryTest(@Nullable String memberToImplement) {
|
||||
doDirectoryTest(new OverrideMethodsHandler(), memberToImplement);
|
||||
}
|
||||
|
||||
protected void doImplementJavaDirectoryTest(String className, String methodName) {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "");
|
||||
myFixture.configureFromTempProjectFile("foo/JavaClass.java");
|
||||
|
||||
Project project = myFixture.getProject();
|
||||
|
||||
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
|
||||
Assert.assertNotNull("Can't find class: " + className, aClass);
|
||||
|
||||
PsiMethod method = aClass.findMethodsByName(methodName, false)[0];
|
||||
Assert.assertNotNull(String.format("Can't find method '%s' in class %s", methodName, className), method);
|
||||
|
||||
generateImplementation(method);
|
||||
|
||||
myFixture.checkResultByFile(getTestName(true) + "/foo/JavaClass.java.after");
|
||||
}
|
||||
|
||||
private void doFileTest(OverrideImplementMethodsHandler handler) {
|
||||
myFixture.configureByFile(getTestName(true) + ".kt");
|
||||
doOverrideImplement(handler, null);
|
||||
checkResultByFile(getTestName(true) + ".kt.after");
|
||||
}
|
||||
|
||||
private void doMultiFileTest(OverrideImplementMethodsHandler handler) {
|
||||
myFixture.configureByFile(getTestName(true) + ".kt");
|
||||
doMultiOverrideImplement(handler);
|
||||
checkResultByFile(getTestName(true) + ".kt.after");
|
||||
}
|
||||
|
||||
protected void doDirectoryTest(OverrideImplementMethodsHandler handler) {
|
||||
doDirectoryTest(handler, null);
|
||||
}
|
||||
|
||||
private void doDirectoryTest(OverrideImplementMethodsHandler handler, @Nullable String memberToOverride) {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "");
|
||||
myFixture.configureFromTempProjectFile("foo/Impl.kt");
|
||||
doOverrideImplement(handler, memberToOverride);
|
||||
checkResultByFile(getTestName(true) + "/foo/Impl.kt.after");
|
||||
}
|
||||
|
||||
private void doOverrideImplement(OverrideImplementMethodsHandler handler, @Nullable String memberToOverride) {
|
||||
PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset());
|
||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
|
||||
assertNotNull("Caret should be inside class or object", classOrObject);
|
||||
|
||||
JetFile jetFile = classOrObject.getContainingJetFile();
|
||||
Set<CallableMemberDescriptor> descriptors = handler.collectMethodsToGenerate(classOrObject);
|
||||
|
||||
CallableMemberDescriptor singleToOverride;
|
||||
if (memberToOverride == null) {
|
||||
// Filter out fake overrides of members of Any (equals, hashCode, toString)
|
||||
List<CallableMemberDescriptor> filtered = KotlinPackage.filter(descriptors, new Function1<CallableMemberDescriptor, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(CallableMemberDescriptor descriptor) {
|
||||
ClassDescriptor any = KotlinBuiltIns.getInstance().getAny();
|
||||
for (CallableMemberDescriptor overridden : OverrideResolver.getOverriddenDeclarations(descriptor)) {
|
||||
if (overridden.getContainingDeclaration().equals(any)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
assertEquals("Invalid number of available descriptors for override", 1, filtered.size());
|
||||
singleToOverride = filtered.iterator().next();
|
||||
}
|
||||
else {
|
||||
CallableMemberDescriptor candidateToOverride = null;
|
||||
for (CallableMemberDescriptor callable : descriptors) {
|
||||
if (callable.getName().asString().equals(memberToOverride)) {
|
||||
if (candidateToOverride != null) {
|
||||
throw new IllegalStateException("more then one descriptor with name " + memberToOverride);
|
||||
}
|
||||
candidateToOverride = callable;
|
||||
}
|
||||
}
|
||||
if (candidateToOverride == null) {
|
||||
throw new IllegalStateException("no descriptors to override with name " + memberToOverride + " found");
|
||||
}
|
||||
singleToOverride = candidateToOverride;
|
||||
}
|
||||
|
||||
performGenerateCommand(classOrObject,
|
||||
OverrideImplementMethodsHandler.Companion.membersFromDescriptors(jetFile, Collections.singletonList(singleToOverride)));
|
||||
}
|
||||
|
||||
private void doMultiOverrideImplement(OverrideImplementMethodsHandler handler) {
|
||||
PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset());
|
||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
|
||||
assertNotNull("Caret should be inside class or object", classOrObject);
|
||||
|
||||
JetFile jetFile = classOrObject.getContainingJetFile();
|
||||
Set<CallableMemberDescriptor> descriptors = handler.collectMethodsToGenerate(classOrObject);
|
||||
|
||||
List<CallableMemberDescriptor> descriptorsList = new ArrayList<CallableMemberDescriptor>(descriptors);
|
||||
Collections.sort(descriptorsList, new Comparator<CallableMemberDescriptor>() {
|
||||
@Override
|
||||
public int compare(@NotNull CallableMemberDescriptor desc1, @NotNull CallableMemberDescriptor desc2) {
|
||||
return desc1.getName().compareTo(desc2.getName());
|
||||
}
|
||||
});
|
||||
|
||||
performGenerateCommand(classOrObject, OverrideImplementMethodsHandler.Companion.membersFromDescriptors(jetFile, descriptorsList));
|
||||
}
|
||||
|
||||
private void generateImplementation(@NotNull final PsiMethod method) {
|
||||
WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PsiClass aClass = ((PsiClassOwner) myFixture.getFile()).getClasses()[0];
|
||||
|
||||
PsiMethodMember methodMember = new PsiMethodMember(method, PsiSubstitutor.EMPTY);
|
||||
|
||||
OverrideImplementUtil.overrideOrImplementMethodsInRightPlace(
|
||||
myFixture.getEditor(), aClass, new SmartList<PsiMethodMember>(methodMember), false);
|
||||
|
||||
PostprocessReformattingAspect.getInstance(myFixture.getProject()).doPostponedFormatting();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void performGenerateCommand(
|
||||
final JetClassOrObject classOrObject,
|
||||
final List<DescriptorClassMember> descriptorsToGenerate
|
||||
) {
|
||||
try {
|
||||
new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) {
|
||||
@Override
|
||||
protected void run(@NotNull Result result) throws Throwable {
|
||||
OverrideImplementMethodsHandler.Companion.generateMethods(myFixture.getEditor(), classOrObject, descriptorsToGenerate);
|
||||
}
|
||||
}.performCommand();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
throw UtilsPackage.rethrow(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkResultByFile(String fileName) {
|
||||
File expectedFile = new File(myFixture.getTestDataPath(), fileName);
|
||||
try {
|
||||
Assert.assertTrue(expectedFile.exists());
|
||||
myFixture.checkResultByFile(fileName);
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
JetTestUtils.assertEqualsToFile(expectedFile, TagsTestDataUtil.generateTextWithCaretAndSelection(myFixture.getEditor()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil
|
||||
import com.intellij.codeInsight.generation.PsiMethodMember
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClassOwner
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiSubstitutor
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMethodsHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMethodsHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMethodsHandler
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
import org.jetbrains.kotlin.test.TagsTestDataUtil
|
||||
import org.jetbrains.kotlin.utils.rethrow
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
public abstract class AbstractOverrideImplementTest : JetLightCodeInsightFixtureTestCase() {
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = JetLightProjectDescriptor.INSTANCE
|
||||
|
||||
protected fun doImplementFileTest() {
|
||||
doFileTest(ImplementMethodsHandler())
|
||||
}
|
||||
|
||||
protected fun doOverrideFileTest() {
|
||||
doFileTest(OverrideMethodsHandler())
|
||||
}
|
||||
|
||||
protected fun doMultiImplementFileTest() {
|
||||
doMultiFileTest(ImplementMethodsHandler())
|
||||
}
|
||||
|
||||
protected fun doMultiOverrideFileTest() {
|
||||
doMultiFileTest(OverrideMethodsHandler())
|
||||
}
|
||||
|
||||
protected fun doImplementDirectoryTest() {
|
||||
doDirectoryTest(ImplementMethodsHandler())
|
||||
}
|
||||
|
||||
protected fun doOverrideDirectoryTest(memberToImplement: String?) {
|
||||
doDirectoryTest(OverrideMethodsHandler(), memberToImplement)
|
||||
}
|
||||
|
||||
protected fun doImplementJavaDirectoryTest(className: String, methodName: String) {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "")
|
||||
myFixture.configureFromTempProjectFile("foo/JavaClass.java")
|
||||
|
||||
val project = myFixture.project
|
||||
|
||||
val aClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project))
|
||||
?: error("Can't find class: $className")
|
||||
|
||||
val method = aClass.findMethodsByName(methodName, false)[0]
|
||||
?: error("Can't find method '$methodName' in class $className")
|
||||
|
||||
generateImplementation(method)
|
||||
|
||||
myFixture.checkResultByFile(getTestName(true) + "/foo/JavaClass.java.after")
|
||||
}
|
||||
|
||||
private fun doFileTest(handler: OverrideImplementMethodsHandler) {
|
||||
myFixture.configureByFile(getTestName(true) + ".kt")
|
||||
doOverrideImplement(handler, null)
|
||||
checkResultByFile(getTestName(true) + ".kt.after")
|
||||
}
|
||||
|
||||
private fun doMultiFileTest(handler: OverrideImplementMethodsHandler) {
|
||||
myFixture.configureByFile(getTestName(true) + ".kt")
|
||||
doMultiOverrideImplement(handler)
|
||||
checkResultByFile(getTestName(true) + ".kt.after")
|
||||
}
|
||||
|
||||
protected fun doDirectoryTest(handler: OverrideImplementMethodsHandler) {
|
||||
doDirectoryTest(handler, null)
|
||||
}
|
||||
|
||||
private fun doDirectoryTest(handler: OverrideImplementMethodsHandler, memberToOverride: String?) {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "")
|
||||
myFixture.configureFromTempProjectFile("foo/Impl.kt")
|
||||
doOverrideImplement(handler, memberToOverride)
|
||||
checkResultByFile(getTestName(true) + "/foo/Impl.kt.after")
|
||||
}
|
||||
|
||||
private fun doOverrideImplement(handler: OverrideImplementMethodsHandler, memberToOverride: String?) {
|
||||
val elementAtCaret = myFixture.file.findElementAt(myFixture.editor.caretModel.offset)
|
||||
val classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, javaClass<JetClassOrObject>())
|
||||
?: error("Caret should be inside class or object")
|
||||
|
||||
val chooserObjects = handler.collectMethodsToGenerate(classOrObject)
|
||||
|
||||
val singleToOverride = if (memberToOverride == null) {
|
||||
val filtered = chooserObjects.filter { it.descriptor.containingDeclaration != KotlinBuiltIns.getInstance().any }
|
||||
assertEquals(1, filtered.size(), "Invalid number of available chooserObjects for override")
|
||||
filtered.single()
|
||||
}
|
||||
else {
|
||||
var candidateToOverride: OverrideMemberChooserObject? = null
|
||||
for (chooserObject in chooserObjects) {
|
||||
if (chooserObject.descriptor.name.asString() == memberToOverride) {
|
||||
if (candidateToOverride != null) {
|
||||
throw IllegalStateException("more then one descriptor with name $memberToOverride")
|
||||
}
|
||||
candidateToOverride = chooserObject
|
||||
}
|
||||
}
|
||||
if (candidateToOverride == null) {
|
||||
throw IllegalStateException("no chooserObjects to override with name $memberToOverride found")
|
||||
}
|
||||
candidateToOverride!!
|
||||
}
|
||||
|
||||
performGenerateCommand(classOrObject, listOf(singleToOverride))
|
||||
}
|
||||
|
||||
private fun doMultiOverrideImplement(handler: OverrideImplementMethodsHandler) {
|
||||
val elementAtCaret = myFixture.file.findElementAt(myFixture.editor.caretModel.offset)
|
||||
val classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, javaClass<JetClassOrObject>())
|
||||
?: error("Caret should be inside class or object")
|
||||
|
||||
val chooserObjects = handler.collectMethodsToGenerate(classOrObject).sortBy { it.member.name.asString() + " in " + it.immediateSuper.containingDeclaration.name.asString() }
|
||||
performGenerateCommand(classOrObject, chooserObjects)
|
||||
}
|
||||
|
||||
private fun generateImplementation(method: PsiMethod) {
|
||||
getProject().executeWriteCommand("") {
|
||||
val aClass = (myFixture.file as PsiClassOwner).classes[0]
|
||||
|
||||
val methodMember = PsiMethodMember(method, PsiSubstitutor.EMPTY)
|
||||
|
||||
OverrideImplementUtil.overrideOrImplementMethodsInRightPlace(myFixture.editor, aClass, SmartList(methodMember), false)
|
||||
|
||||
PostprocessReformattingAspect.getInstance(myFixture.project).doPostponedFormatting()
|
||||
}
|
||||
}
|
||||
|
||||
private fun performGenerateCommand(
|
||||
classOrObject: JetClassOrObject,
|
||||
selectedElements: List<OverrideMemberChooserObject>) {
|
||||
try {
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
OverrideImplementMethodsHandler.generateMethods(myFixture.editor, classOrObject, selectedElements)
|
||||
}
|
||||
}
|
||||
catch (throwable: Throwable) {
|
||||
throw rethrow(throwable)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun checkResultByFile(fileName: String) {
|
||||
val expectedFile = File(myFixture.testDataPath, fileName)
|
||||
try {
|
||||
Assert.assertTrue(expectedFile.exists())
|
||||
myFixture.checkResultByFile(fileName)
|
||||
}
|
||||
catch (error: AssertionError) {
|
||||
JetTestUtils.assertEqualsToFile(expectedFile, TagsTestDataUtil.generateTextWithCaretAndSelection(myFixture.editor))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,4 +198,16 @@ public final class OverrideImplementTest extends AbstractOverrideImplementTest {
|
||||
public void testVarArgs() {
|
||||
doOverrideFileTest();
|
||||
}
|
||||
|
||||
public void testDoNotOverrideFinal() {
|
||||
doMultiOverrideFileTest();
|
||||
}
|
||||
|
||||
public void testSuperPreference() {
|
||||
doMultiOverrideFileTest();
|
||||
}
|
||||
|
||||
public void testAmbiguousSuper() {
|
||||
doMultiOverrideFileTest();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user