Initial implementation of KT-5955 Completion for override methods

#KT-5955 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-09-24 12:33:59 +03:00
parent 15edbbb60c
commit 1c6ca9d036
31 changed files with 599 additions and 117 deletions
@@ -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
}
}
@@ -257,6 +257,12 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
}
}
"override" -> {
collector.addElement(lookupElement)
OverridesCompletion(collector, lookupElementFactory).complete(position)
}
else -> collector.addElement(lookupElement)
}
}
@@ -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" }
@@ -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) {...}"
@@ -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 {...}"
@@ -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 {...}"
@@ -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 {...}"
@@ -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"
@@ -0,0 +1,8 @@
interface I {
val someVal: java.io.File?
}
class A(public ov<caret>) : I {
}
// ELEMENT_TEXT: "override val someVal: File?"
@@ -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?"
@@ -0,0 +1,8 @@
interface I {
val someVal: java.io.File?
}
class A(ov<caret>) : I {
}
// ELEMENT_TEXT: "override val someVal: File?"
@@ -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?"
@@ -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)
@@ -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)
@@ -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)
@@ -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()
}
}
@@ -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) {