Initial implementation of KT-5955 Completion for override methods
#KT-5955 Fixed
This commit is contained in:
@@ -17,16 +17,27 @@
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.psi.JetDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import org.jetbrains.kotlin.psi.JetWithExpressionInitializer
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.SmartPointerManager
|
||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
||||
import org.jetbrains.kotlin.psi.JetDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.JetProperty
|
||||
import org.jetbrains.kotlin.psi.JetWithExpressionInitializer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
|
||||
public fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement): Boolean {
|
||||
public fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement) {
|
||||
val project = element.project
|
||||
val pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(element)
|
||||
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
|
||||
pointer.element?.let { moveCaretIntoGeneratedElementDocumentUnblocked(editor, it) }
|
||||
}
|
||||
|
||||
private fun moveCaretIntoGeneratedElementDocumentUnblocked(editor: Editor, element: PsiElement): Boolean {
|
||||
// Inspired by GenerateMembersUtils.positionCaret()
|
||||
|
||||
if (element is JetDeclarationWithBody && element.hasBody()) {
|
||||
@@ -72,7 +83,7 @@ public fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement): B
|
||||
|
||||
if (element is JetProperty) {
|
||||
for (accessor in element.getAccessors()) {
|
||||
if (moveCaretIntoGeneratedElement(editor, accessor)) {
|
||||
if (moveCaretIntoGeneratedElementDocumentUnblocked(editor, accessor)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -257,6 +257,12 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
"override" -> {
|
||||
collector.addElement(lookupElement)
|
||||
|
||||
OverridesCompletion(collector, lookupElementFactory).complete(position)
|
||||
}
|
||||
|
||||
else -> collector.addElement(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -395,7 +395,7 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
if (parent is JetParameterList) {
|
||||
val balance = countParenthesisBalance(tokenBefore, parent)
|
||||
val count = if (balance > 1) balance - 1 else 0
|
||||
return CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ")".repeat(count) + " a: B$"
|
||||
return CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ")".repeat(count) + " a: B"
|
||||
}
|
||||
if (parent is JetTypeElement) return null
|
||||
if (parent is JetAnnotationEntry) return null
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.ui.RowIcon
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.quickfix.moveCaret
|
||||
import org.jetbrains.kotlin.idea.quickfix.moveCaretIntoGeneratedElement
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
|
||||
class OverridesCompletion(
|
||||
private val collector: LookupElementsCollector,
|
||||
private val lookupElementFactory: LookupElementFactory
|
||||
) {
|
||||
private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
|
||||
modifiers = emptySet()
|
||||
}
|
||||
|
||||
fun complete(position: PsiElement) {
|
||||
val isConstructorParameter = position.getNonStrictParentOfType<JetPrimaryConstructor>() != null
|
||||
|
||||
val classOrObject = position.getNonStrictParentOfType<JetClassOrObject>() ?: return
|
||||
|
||||
val members = OverrideMembersHandler().collectMembersToGenerate(classOrObject)
|
||||
|
||||
for (memberObject in members) {
|
||||
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
|
||||
|
||||
val descriptor = memberObject.descriptor
|
||||
var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
|
||||
|
||||
var text = "override " + PRESENTATION_RENDERER.render(descriptor)
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
text += " {...}"
|
||||
}
|
||||
|
||||
val baseClass = descriptor.containingDeclaration as ClassDescriptor
|
||||
val baseClassName = baseClass.name.asString()
|
||||
|
||||
val baseIcon = (lookupElement.`object` as DeclarationLookupObject).getIcon(0)
|
||||
val additionalIcon = if (descriptor.modality == Modality.ABSTRACT)
|
||||
AllIcons.Gutter.ImplementingMethod
|
||||
else
|
||||
AllIcons.Gutter.OverridingMethod
|
||||
val icon = RowIcon(baseIcon, additionalIcon)
|
||||
|
||||
val baseClassDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(position.project, baseClass)
|
||||
val baseClassIcon = JetDescriptorIconProvider.getIcon(baseClass, baseClassDeclaration, 0)
|
||||
|
||||
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = "override"
|
||||
override fun getAllLookupStrings() = setOf(lookupString)
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
super.renderElement(presentation)
|
||||
|
||||
presentation.itemText = text
|
||||
presentation.icon = icon
|
||||
presentation.clearTail()
|
||||
presentation.setTypeText(baseClassName, baseClassIcon)
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val dummyMemberText = if (isConstructorParameter) "override val dummy" else "override fun dummy() {}"
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, dummyMemberText)
|
||||
|
||||
PsiDocumentManager.getInstance(context.project).commitAllDocuments()
|
||||
|
||||
val dummyMember = context.file.findElementAt(context.startOffset)!!.getStrictParentOfType<JetNamedDeclaration>()!!
|
||||
|
||||
// keep original modifiers
|
||||
val modifierList = JetPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text)
|
||||
|
||||
val prototype = memberObject.generateMember(context.project, isConstructorParameter)
|
||||
prototype.modifierList!!.replace(modifierList)
|
||||
val insertedMember = dummyMember.replaced(prototype)
|
||||
|
||||
ShortenReferences.DEFAULT.process(insertedMember)
|
||||
|
||||
if (isConstructorParameter) {
|
||||
context.editor.moveCaret(insertedMember.endOffset)
|
||||
}
|
||||
else {
|
||||
moveCaretIntoGeneratedElement(context.editor, insertedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collector.addElement(lookupElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
interface I<T> {
|
||||
fun foo(t: T): T
|
||||
}
|
||||
|
||||
class A<T> : List<String>, I<T> {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "override", itemText: "override" }
|
||||
// EXIST: { itemText: "override fun hashCode(): Int {...}", tailText: null, typeText: "Any" }
|
||||
// EXIST: { itemText: "override fun foo(t: T): T {...}", tailText: null, typeText: "I" }
|
||||
// EXIST: { itemText: "override fun get(index: Int): String {...}", tailText: null, typeText: "List" }
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
interface I {
|
||||
fun foo()
|
||||
val someVal: Int
|
||||
var someVar: Int
|
||||
}
|
||||
|
||||
class Base1 {
|
||||
protected open fun bar(){}
|
||||
open val fromBase: String = ""
|
||||
}
|
||||
|
||||
open class Base2 : Base1() {
|
||||
}
|
||||
|
||||
class A(overr<caret>) : Base2(), I
|
||||
|
||||
// EXIST: { lookupString: "override", itemText: "override" }
|
||||
// EXIST: { itemText: "override val someVal: Int", tailText: null, typeText: "I" }
|
||||
// EXIST: { itemText: "override var someVar: Int", tailText: null, typeText: "I" }
|
||||
// EXIST: { itemText: "override val fromBase: String", tailText: null, typeText: "Base1" }
|
||||
// EXIST_JAVA_ONLY: { lookupString: "override", itemText: "override: Override" }
|
||||
// NOTHING_ELSE
|
||||
@@ -0,0 +1,24 @@
|
||||
interface I {
|
||||
fun foo()
|
||||
val someVal: Int
|
||||
var someVar: Int
|
||||
}
|
||||
|
||||
class Base1 {
|
||||
protected open fun bar(){}
|
||||
}
|
||||
|
||||
open class Base2 : Base1() {
|
||||
}
|
||||
|
||||
class A : Base2(), I {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "override", itemText: "override" }
|
||||
// EXIST: { itemText: "override fun bar() {...}", lookupString: "override", tailText: null, typeText: "Base1" }
|
||||
// EXIST: { itemText: "override fun equals(other: Any?): Boolean {...}", lookupString: "override", tailText: null, typeText: "Any" }
|
||||
// EXIST: { itemText: "override fun foo() {...}", lookupString: "override", tailText: null, typeText: "I" }
|
||||
// EXIST: { itemText: "override fun hashCode(): Int {...}", lookupString: "override", tailText: null, typeText: "Any" }
|
||||
// EXIST: { itemText: "override val someVal: Int", lookupString: "override", tailText: null, typeText: "I" }
|
||||
// EXIST: { itemText: "override var someVar: Int", lookupString: "override", tailText: null, typeText: "I" }
|
||||
@@ -0,0 +1,9 @@
|
||||
interface I {
|
||||
fun foo(p: Int)
|
||||
}
|
||||
|
||||
class A : I {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun foo(p: Int) {...}"
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface I {
|
||||
fun foo(p: Int)
|
||||
}
|
||||
|
||||
class A : I {
|
||||
override fun foo(p: Int) {
|
||||
<caret><selection>throw UnsupportedOperationException()</selection>
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun foo(p: Int) {...}"
|
||||
@@ -0,0 +1,9 @@
|
||||
interface I {
|
||||
val someVal: String?
|
||||
}
|
||||
|
||||
class A : I {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: String?"
|
||||
@@ -0,0 +1,10 @@
|
||||
interface I {
|
||||
val someVal: String?
|
||||
}
|
||||
|
||||
class A : I {
|
||||
override val someVal: String?
|
||||
get() = <caret><selection>throw UnsupportedOperationException()</selection>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: String?"
|
||||
@@ -0,0 +1,9 @@
|
||||
interface I {
|
||||
var someVar: String
|
||||
}
|
||||
|
||||
class A : I {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override var someVar: String"
|
||||
@@ -0,0 +1,12 @@
|
||||
interface I {
|
||||
var someVar: String
|
||||
}
|
||||
|
||||
class A : I {
|
||||
override var someVar: String
|
||||
get() = <caret><selection>throw UnsupportedOperationException()</selection>
|
||||
set(value) {
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override var someVar: String"
|
||||
@@ -0,0 +1,6 @@
|
||||
class A {
|
||||
@Deprecated("")
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
@Deprecated("")
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<caret><selection>return super.equals(other)</selection>
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
@@ -0,0 +1,6 @@
|
||||
class A {
|
||||
@Deprecated("") // it is deprecated
|
||||
public o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
@Deprecated("") // it is deprecated
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
<caret><selection>return super.equals(other)</selection>
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<caret><selection>return super.equals(other)</selection>
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override fun equals(other: Any?): Boolean {...}"
|
||||
@@ -0,0 +1,9 @@
|
||||
open class B {
|
||||
open var someVar: String = ""
|
||||
}
|
||||
|
||||
class A : B {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override var someVar: String"
|
||||
@@ -0,0 +1,12 @@
|
||||
open class B {
|
||||
open var someVar: String = ""
|
||||
}
|
||||
|
||||
class A : B {
|
||||
override var someVar: String
|
||||
get() = <caret><selection>super.someVar</selection>
|
||||
set(value) {
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override var someVar: String"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
class A(public ov<caret>) : I {
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: File?"
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import java.io.File
|
||||
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
class A(public override val someVal: File?<caret>) : I {
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: File?"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
class A(ov<caret>) : I {
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: File?"
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import java.io.File
|
||||
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
class A(override val someVal: File?<caret>) : I {
|
||||
}
|
||||
|
||||
// ELEMENT_TEXT: "override val someVal: File?"
|
||||
+27
@@ -1591,6 +1591,33 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/override")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Override extends AbstractJSBasicCompletionTest {
|
||||
public void testAllFilesPresentInOverride() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/override"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("Generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/Generics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InConstructorParameters.kt")
|
||||
public void testInConstructorParameters() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/InConstructorParameters.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/Simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+27
@@ -1591,6 +1591,33 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/override")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Override extends AbstractJvmBasicCompletionTest {
|
||||
public void testAllFilesPresentInOverride() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/override"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("Generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/Generics.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InConstructorParameters.kt")
|
||||
public void testInConstructorParameters() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/InConstructorParameters.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/override/Simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+63
@@ -311,6 +311,69 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/handlers/basic/override")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Override extends AbstractBasicCompletionHandlerTest {
|
||||
public void testAllFilesPresentInOverride() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/override"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ImplementFunction.kt")
|
||||
public void testImplementFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/ImplementFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ImplementVal.kt")
|
||||
public void testImplementVal() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/ImplementVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ImplementVar.kt")
|
||||
public void testImplementVar() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/ImplementVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("KeepAnnotationBefore.kt")
|
||||
public void testKeepAnnotationBefore() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/KeepAnnotationBefore.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("KeepModifiersBefore.kt")
|
||||
public void testKeepModifiersBefore() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/KeepModifiersBefore.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OverrideFunction.kt")
|
||||
public void testOverrideFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/OverrideFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OverrideVar.kt")
|
||||
public void testOverrideVar() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/OverrideVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PublicValInConstructorParameter.kt")
|
||||
public void testPublicValInConstructorParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/PublicValInConstructorParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ValInConstructorParameter.kt")
|
||||
public void testValInConstructorParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/override/ValInConstructorParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+17
-105
@@ -23,18 +23,18 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.quickfix.moveCaretIntoGeneratedElement
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.renderer.*
|
||||
import java.util.*
|
||||
|
||||
public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandler {
|
||||
@@ -86,7 +86,7 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
generateMethods(editor, classOrObject, selectedElements)
|
||||
generateMembers(editor, classOrObject, selectedElements)
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile) = invoke(project, editor, file, false)
|
||||
@@ -94,50 +94,33 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
|
||||
companion object {
|
||||
private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions {
|
||||
renderDefaultValues = false
|
||||
modifiers = setOf(DescriptorRendererModifier.OVERRIDE)
|
||||
withDefinedIn = false
|
||||
nameShortness = NameShortness.SOURCE_CODE_QUALIFIED
|
||||
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
|
||||
unitReturnType = false
|
||||
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||
}
|
||||
|
||||
public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
|
||||
public fun generateMembers(editor: Editor, classOrObject: JetClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
|
||||
runWriteAction {
|
||||
if (selectedElements.isEmpty()) return@runWriteAction
|
||||
|
||||
val body = classOrObject.getOrCreateBody()
|
||||
|
||||
var afterAnchor = findInsertAfterAnchor(editor, body) ?: return@runWriteAction
|
||||
|
||||
var firstGenerated: PsiElement? = null
|
||||
|
||||
val elementsToCompact = ArrayList<JetElement>()
|
||||
for (element in generateOverridingMembers(selectedElements, classOrObject)) {
|
||||
val added = body.addAfter(element, afterAnchor)
|
||||
val project = classOrObject.project
|
||||
val insertedMembers = ArrayList<JetCallableDeclaration>()
|
||||
for (memberObject in selectedElements) {
|
||||
val member = memberObject.generateMember(project)
|
||||
val added = body.addAfter(member, afterAnchor) as JetCallableDeclaration
|
||||
|
||||
if (firstGenerated == null) {
|
||||
firstGenerated = added
|
||||
}
|
||||
|
||||
afterAnchor = added
|
||||
elementsToCompact.add(added as JetElement)
|
||||
insertedMembers.add(added)
|
||||
}
|
||||
|
||||
ShortenReferences.Companion.DEFAULT.process(elementsToCompact)
|
||||
|
||||
if (firstGenerated == null) return@runWriteAction
|
||||
|
||||
val project = classOrObject.project
|
||||
val pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(firstGenerated)
|
||||
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
|
||||
val element = pointer.element
|
||||
if (element != null) {
|
||||
moveCaretIntoGeneratedElement(editor, element)
|
||||
}
|
||||
ShortenReferences.DEFAULT.process(insertedMembers)
|
||||
|
||||
moveCaretIntoGeneratedElement(editor, firstGenerated!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,76 +165,5 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
|
||||
|
||||
return whiteSpace
|
||||
}
|
||||
|
||||
private fun generateOverridingMembers(selectedElements: Collection<OverrideMemberChooserObject>,
|
||||
classOrObject: JetClassOrObject): List<JetElement> {
|
||||
val overridingMembers = ArrayList<JetElement>()
|
||||
for (selectedElement in selectedElements) {
|
||||
val descriptor = selectedElement.immediateSuper
|
||||
when (descriptor) {
|
||||
is SimpleFunctionDescriptor -> overridingMembers.add(overrideFunction(classOrObject, descriptor, selectedElement.bodyType))
|
||||
is PropertyDescriptor -> overridingMembers.add(overrideProperty(classOrObject, descriptor, selectedElement.bodyType))
|
||||
else -> error("Unknown member to override: $descriptor")
|
||||
}
|
||||
}
|
||||
return overridingMembers
|
||||
}
|
||||
|
||||
private fun overrideProperty(classOrObject: JetClassOrObject, descriptor: PropertyDescriptor, bodyType: OverrideMemberChooserObject.BodyType): JetElement {
|
||||
val newDescriptor = descriptor.copy(descriptor.containingDeclaration, Modality.OPEN, descriptor.visibility,
|
||||
descriptor.kind, /* copyOverrides = */ true) as PropertyDescriptor
|
||||
newDescriptor.addOverriddenDescriptor(descriptor)
|
||||
|
||||
val body = StringBuilder {
|
||||
append("\nget()")
|
||||
append(" = ")
|
||||
append(generateUnsupportedOrSuperCall(descriptor, bodyType))
|
||||
if (descriptor.isVar) {
|
||||
append("\nset(value) {}")
|
||||
}
|
||||
}
|
||||
return JetPsiFactory(classOrObject.project).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body)
|
||||
}
|
||||
|
||||
private fun overrideFunction(classOrObject: JetClassOrObject, descriptor: FunctionDescriptor, bodyType: OverrideMemberChooserObject.BodyType): JetNamedFunction {
|
||||
val newDescriptor = descriptor.copy(descriptor.containingDeclaration, Modality.OPEN, descriptor.visibility,
|
||||
descriptor.kind, /* copyOverrides = */ true)
|
||||
newDescriptor.addOverriddenDescriptor(descriptor)
|
||||
|
||||
val returnType = descriptor.returnType
|
||||
val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType)
|
||||
|
||||
val delegation = generateUnsupportedOrSuperCall(descriptor, bodyType)
|
||||
|
||||
val body = "{" + (if (returnsNotUnit && bodyType != OverrideMemberChooserObject.BodyType.EMPTY) "return " else "") + delegation + "}"
|
||||
|
||||
return JetPsiFactory(classOrObject.project).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body)
|
||||
}
|
||||
|
||||
private fun generateUnsupportedOrSuperCall(descriptor: CallableMemberDescriptor, bodyType: OverrideMemberChooserObject.BodyType): String {
|
||||
if (bodyType == OverrideMemberChooserObject.BodyType.EMPTY) {
|
||||
return "throw UnsupportedOperationException()"
|
||||
}
|
||||
else {
|
||||
return StringBuilder {
|
||||
append("super")
|
||||
if (bodyType == OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER) {
|
||||
val superClassFqName = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor.containingDeclaration as ClassifierDescriptor)
|
||||
append("<").append(superClassFqName).append(">")
|
||||
}
|
||||
append(".").append(descriptor.escapedName())
|
||||
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
val paramTexts = descriptor.valueParameters.map {
|
||||
val renderedName = it.escapedName()
|
||||
if (it.varargElementType != null) "*$renderedName" else renderedName
|
||||
}
|
||||
paramTexts.joinTo(this, prefix="(", postfix=")")
|
||||
}
|
||||
}.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.escapedName() = name.render()
|
||||
}
|
||||
}
|
||||
|
||||
+94
-3
@@ -21,10 +21,13 @@ import com.intellij.codeInsight.generation.MemberChooserObject
|
||||
import com.intellij.codeInsight.generation.MemberChooserObjectBase
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.renderer.*
|
||||
|
||||
interface OverrideMemberChooserObject : ClassMember {
|
||||
enum class BodyType {
|
||||
@@ -56,7 +59,7 @@ interface OverrideMemberChooserObject : ClassMember {
|
||||
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
|
||||
|
||||
override val descriptor: CallableMemberDescriptor
|
||||
get() = super<DescriptorMemberChooserObject>.descriptor as CallableMemberDescriptor
|
||||
get() = super.descriptor as CallableMemberDescriptor
|
||||
}
|
||||
|
||||
private class WithoutDeclaration(
|
||||
@@ -72,3 +75,91 @@ interface OverrideMemberChooserObject : ClassMember {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun OverrideMemberChooserObject.generateMember(project: Project, asConstructorParameter: Boolean = false): JetCallableDeclaration {
|
||||
val descriptor = immediateSuper
|
||||
if (asConstructorParameter) {
|
||||
assert(descriptor is PropertyDescriptor) { "asConstructorParameter is valid only for PropertyDescriptor" }
|
||||
return generateConstructorParameter(project, descriptor as PropertyDescriptor)
|
||||
}
|
||||
|
||||
return when (descriptor) {
|
||||
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)
|
||||
is PropertyDescriptor -> generateProperty(project, descriptor, bodyType)
|
||||
else -> error("Unknown member to override: $descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions {
|
||||
renderDefaultValues = false
|
||||
modifiers = setOf(DescriptorRendererModifier.OVERRIDE)
|
||||
withDefinedIn = false
|
||||
nameShortness = NameShortness.SOURCE_CODE_QUALIFIED
|
||||
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
|
||||
unitReturnType = false
|
||||
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||
}
|
||||
|
||||
private fun generateProperty(project: Project, descriptor: PropertyDescriptor, bodyType: OverrideMemberChooserObject.BodyType): JetProperty {
|
||||
val newDescriptor = descriptor.copy(descriptor.containingDeclaration, Modality.OPEN, descriptor.visibility,
|
||||
descriptor.kind, /* copyOverrides = */ true) as PropertyDescriptor
|
||||
newDescriptor.addOverriddenDescriptor(descriptor)
|
||||
|
||||
val body = StringBuilder {
|
||||
append("\nget()")
|
||||
append(" = ")
|
||||
append(generateUnsupportedOrSuperCall(descriptor, bodyType))
|
||||
if (descriptor.isVar) {
|
||||
append("\nset(value) {}")
|
||||
}
|
||||
}
|
||||
return JetPsiFactory(project).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body)
|
||||
}
|
||||
|
||||
private fun generateConstructorParameter(project: Project, descriptor: PropertyDescriptor): JetParameter {
|
||||
val newDescriptor = descriptor.copy(descriptor.containingDeclaration, Modality.OPEN, descriptor.visibility,
|
||||
descriptor.kind, /* copyOverrides = */ true) as PropertyDescriptor
|
||||
newDescriptor.addOverriddenDescriptor(descriptor)
|
||||
return JetPsiFactory(project).createParameter(OVERRIDE_RENDERER.render(newDescriptor))
|
||||
}
|
||||
|
||||
private fun generateFunction(project: Project, descriptor: FunctionDescriptor, bodyType: OverrideMemberChooserObject.BodyType): JetNamedFunction {
|
||||
val newDescriptor = descriptor.copy(descriptor.containingDeclaration, Modality.OPEN, descriptor.visibility,
|
||||
descriptor.kind, /* copyOverrides = */ true)
|
||||
newDescriptor.addOverriddenDescriptor(descriptor)
|
||||
|
||||
val returnType = descriptor.returnType
|
||||
val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType)
|
||||
|
||||
val delegation = generateUnsupportedOrSuperCall(descriptor, bodyType)
|
||||
|
||||
val body = "{" + (if (returnsNotUnit && bodyType != OverrideMemberChooserObject.BodyType.EMPTY) "return " else "") + delegation + "}"
|
||||
|
||||
return JetPsiFactory(project).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body)
|
||||
}
|
||||
|
||||
private fun generateUnsupportedOrSuperCall(descriptor: CallableMemberDescriptor, bodyType: OverrideMemberChooserObject.BodyType): String {
|
||||
if (bodyType == OverrideMemberChooserObject.BodyType.EMPTY) {
|
||||
return "throw UnsupportedOperationException()"
|
||||
}
|
||||
else {
|
||||
return StringBuilder {
|
||||
append("super")
|
||||
if (bodyType == OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER) {
|
||||
val superClassFqName = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor.containingDeclaration as ClassifierDescriptor)
|
||||
append("<").append(superClassFqName).append(">")
|
||||
}
|
||||
append(".").append(descriptor.name.render())
|
||||
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
val paramTexts = descriptor.valueParameters.map {
|
||||
val renderedName = it.name.render()
|
||||
if (it.varargElementType != null) "*$renderedName" else renderedName
|
||||
}
|
||||
paramTexts.joinTo(this, prefix="(", postfix=")")
|
||||
}
|
||||
}.toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ public abstract class AbstractOverrideImplementTest : JetLightCodeInsightFixture
|
||||
selectedElements: List<OverrideMemberChooserObject>) {
|
||||
try {
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
OverrideImplementMembersHandler.generateMethods(myFixture.editor, classOrObject, selectedElements)
|
||||
OverrideImplementMembersHandler.generateMembers(myFixture.editor, classOrObject, selectedElements)
|
||||
}
|
||||
}
|
||||
catch (throwable: Throwable) {
|
||||
|
||||
Reference in New Issue
Block a user