Override/Implement members: place members in the same order as super class members

This commit is contained in:
Toshiaki Kameyama
2019-04-05 16:52:35 +09:00
committed by Dmitry Gridin
parent a38651c1ec
commit a3c3ab08fd
18 changed files with 480 additions and 23 deletions
@@ -218,7 +218,8 @@ fun <T : KtDeclaration> insertMembersAfter(
editor: Editor?,
classOrObject: KtClassOrObject,
members: Collection<T>,
anchor: PsiElement? = null
anchor: PsiElement? = null,
getAnchor: (KtDeclaration) -> PsiElement? = { null }
): List<T> {
members.ifEmpty { return emptyList() }
@@ -239,6 +240,8 @@ fun <T : KtDeclaration> insertMembersAfter(
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList<T>()
otherMembers.mapTo(insertedMembers) {
afterAnchor = getAnchor(it) ?: afterAnchor
if (classOrObject is KtClass && classOrObject.isEnum()) {
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { element ->
@@ -25,13 +25,24 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.insertMembersAfter
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.prevSiblingOfSameType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandler {
@@ -42,7 +53,10 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
protected abstract fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject>
private fun showOverrideImplementChooser(project: Project, members: Array<OverrideMemberChooserObject>): MemberChooser<OverrideMemberChooserObject>? {
private fun showOverrideImplementChooser(
project: Project,
members: Array<OverrideMemberChooserObject>
): MemberChooser<OverrideMemberChooserObject>? {
val chooser = MemberChooser(members, true, true, project)
chooser.title = getChooserTitle()
chooser.show()
@@ -80,8 +94,7 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
if (implementAll) {
selectedElements = members
copyDoc = false
}
else {
} else {
val chooser = showOverrideImplementChooser(project, members.toTypedArray()) ?: return
selectedElements = chooser.selectedElements ?: return
copyDoc = chooser.isCopyJavadoc
@@ -101,12 +114,65 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
companion object {
fun generateMembers(
editor: Editor?,
classOrObject: KtClassOrObject,
selectedElements: Collection<OverrideMemberChooserObject>,
copyDoc: Boolean
editor: Editor?,
classOrObject: KtClassOrObject,
selectedElements: Collection<OverrideMemberChooserObject>,
copyDoc: Boolean
) {
insertMembersAfter(editor, classOrObject, selectedElements.map { it.generateMember(classOrObject, copyDoc) })
val selectedMemberDescriptors = selectedElements.associate { it.generateMember(classOrObject, copyDoc) to it.descriptor }
val classBody = classOrObject.body
if (classBody == null) {
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys)
return
}
val offset = editor?.caretModel?.offset ?: classBody.startOffset
val offsetCursorElement = PsiTreeUtil.findFirstParent(classBody.containingFile.findElementAt(offset)) {
it.parent == classBody
}
if (offsetCursorElement != null && offsetCursorElement != classBody.rBrace) {
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys)
return
}
val classLeftBrace = classBody.lBrace
val allSuperMemberDescriptors = selectedMemberDescriptors.values
.mapNotNull { it.containingDeclaration as? ClassDescriptor }
.associateWith {
DescriptorUtils.getAllDescriptors(it.unsubstitutedMemberScope).filterIsInstance<CallableMemberDescriptor>()
}
val implementedElements = mutableMapOf<CallableMemberDescriptor, KtDeclaration>()
fun ClassDescriptor.findElement(memberDescriptor: CallableMemberDescriptor): KtDeclaration? {
return implementedElements[memberDescriptor]
?: (findCallableMemberBySignature(memberDescriptor)?.source?.getPsi() as? KtDeclaration)?.also {
implementedElements[memberDescriptor] = it
}
}
fun getAnchor(selectedElement: KtDeclaration): PsiElement? {
val lastElement = classOrObject.declarations.lastOrNull()
val selectedMemberDescriptor = selectedMemberDescriptors[selectedElement] ?: return lastElement
val superMemberDescriptors = allSuperMemberDescriptors[selectedMemberDescriptor.containingDeclaration] ?: return lastElement
val index = superMemberDescriptors.indexOf(selectedMemberDescriptor)
if (index == -1) return lastElement
val classDescriptor = classOrObject.descriptor as? ClassDescriptor ?: return lastElement
val upperElement = ((index - 1) downTo 0).firstNotNullResult {
classDescriptor.findElement(superMemberDescriptors[it])
}
if (upperElement != null) return upperElement
val lowerElement = ((index + 1) until superMemberDescriptors.size).firstNotNullResult {
classDescriptor.findElement(superMemberDescriptors[it])
}
if (lowerElement != null) return lowerElement.prevSiblingOfSameType() ?: classLeftBrace
return lastElement
}
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys) { getAnchor(it) }
}
}
}
@@ -0,0 +1,8 @@
interface Runnable {
fun run()
}
class <caret>A : Runnable {
fun foo() {
}
}
@@ -0,0 +1,12 @@
interface Runnable {
fun run()
}
class A : Runnable {
fun foo() {
}
override fun run() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
}
@@ -0,0 +1,18 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class <caret>Test : A {
override fun a3() {
}
override fun a6() {
}
}
@@ -0,0 +1,42 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class Test : A {
override fun a1() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
override fun a2() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a3() {
}
override fun a4() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a5() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a6() {
}
override fun a7() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a8() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,18 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class <caret>Test : A {
override fun a6() {
}
override fun a3() {
}
}
@@ -0,0 +1,42 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class Test : A {
override fun a6() {
}
override fun a7() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a8() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a1() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
override fun a2() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a3() {
}
override fun a4() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a5() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,21 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class <caret>Test : A {
override fun a4() {
}
override fun a6() {
}
override fun a2() {
}
}
@@ -0,0 +1,41 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class Test : A {
override fun a4() {
}
override fun a5() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a6() {
}
override fun a7() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a8() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a1() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
override fun a2() {
}
override fun a3() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,27 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class <caret>Test : A {
fun foo() {
}
override fun a3() {
}
fun bar() {
}
override fun a6() {
}
fun baz() {
}
}
@@ -0,0 +1,51 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
class Test : A {
fun foo() {
}
override fun a1() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
override fun a2() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a3() {
}
override fun a4() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a5() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
fun bar() {
}
override fun a6() {
}
override fun a7() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a8() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
fun baz() {
}
}
@@ -0,0 +1,27 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
interface B {
fun b3()
fun b2()
fun b1()
}
class <caret>Test : A, B {
override fun a3() {
}
override fun b2() {
}
override fun a6() {
}
}
@@ -0,0 +1,59 @@
interface A {
fun a1()
fun a2()
fun a3()
fun a4()
fun a5()
fun a6()
fun a7()
fun a8()
}
interface B {
fun b3()
fun b2()
fun b1()
}
class Test : A, B {
override fun a1() {
<selection><caret>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
override fun a2() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a3() {
}
override fun a4() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a5() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun b3() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun b2() {
}
override fun b1() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a6() {
}
override fun a7() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun a8() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -3,6 +3,13 @@ interface Some {
}
class Other {
fun test() {
val a = 1
}
fun otherTest() {
}
override fun equals(other: Any?): Boolean {
<selection><caret>return super.equals(other)</selection>
}
@@ -14,12 +21,5 @@ class Other {
override fun toString(): String {
return super.toString()
}
fun test() {
val a = 1
}
fun otherTest() {
}
}
@@ -7,11 +7,10 @@ interface T<X> {
enum class E : T<Int> {
A, B, C;
override fun foo(x: Int): Int {
<caret><selection>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
val bar = 1
fun baz() = 2
override fun foo(x: Int): Int {
<caret><selection>TODO("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
}
@@ -7,10 +7,9 @@ interface T<X> {
enum class E : T<Int> {
A, B, C;
override val foo: Int
get() = <caret><selection>TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.</selection>
val bar = 1
fun baz() = 2
override val foo: Int
get() = <caret><selection>TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.</selection>
}
@@ -289,4 +289,28 @@ class OverrideImplementTest : AbstractOverrideImplementTest() {
fun testUnresolvedType() {
doOverrideFileTest()
}
fun testImplementFromClassName() {
doMultiImplementFileTest()
}
fun testImplementFromClassName2() {
doMultiImplementFileTest()
}
fun testImplementFromClassName3() {
doMultiImplementFileTest()
}
fun testImplementFromClassName4() {
doMultiImplementFileTest()
}
fun testImplementFromClassName5() {
doMultiImplementFileTest()
}
fun testImplementFromClassName6() {
doMultiImplementFileTest()
}
}