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
@@ -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)