diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt index 3ee7a7f0dca..4cda16f0873 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/quickfix/generateUtil.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.psi.JetWithExpressionInitializer import org.jetbrains.kotlin.psi.JetProperty import org.jetbrains.kotlin.psi.psiUtil.siblings -private fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement): Boolean { +public fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement): Boolean { // Inspired by GenerateMembersUtils.positionCaret() if (element is JetDeclarationWithBody && element.hasBody()) { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.java b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.java deleted file mode 100644 index 8183fb425d7..00000000000 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.core.codeInsight; - -import com.intellij.codeInsight.hint.HintManager; -import com.intellij.ide.util.MemberChooser; -import com.intellij.lang.ASTNode; -import com.intellij.lang.LanguageCodeInsightActionHandler; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde; -import org.jetbrains.kotlin.idea.quickfix.QuickfixPackage; -import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; -import org.jetbrains.kotlin.idea.util.ShortenReferences; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.renderer.DescriptorRenderer; -import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder; -import org.jetbrains.kotlin.renderer.NameShortness; -import org.jetbrains.kotlin.types.JetType; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; - -public abstract class OverrideImplementMethodsHandler implements LanguageCodeInsightActionHandler { - - private static final DescriptorRenderer OVERRIDE_RENDERER = new DescriptorRendererBuilder() - .setRenderDefaultValues(false) - .setModifiers(DescriptorRenderer.Modifier.OVERRIDE) - .setWithDefinedIn(false) - .setNameShortness(NameShortness.SOURCE_CODE_QUALIFIED) - .setOverrideRenderingPolicy(DescriptorRenderer.OverrideRenderingPolicy.RENDER_OVERRIDE) - .setUnitReturnType(false) - .setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES) - .build(); - - private static final Logger LOG = Logger.getInstance(OverrideImplementMethodsHandler.class.getCanonicalName()); - - public static List membersFromDescriptors( - JetFile file, Iterable missingImplementations - ) { - List members = new ArrayList(); - for (CallableMemberDescriptor memberDescriptor : missingImplementations) { - PsiElement declaration = DescriptorToSourceUtilsIde.INSTANCE$.getAnyDeclaration(file.getProject(), memberDescriptor); - if (declaration == null) { - LOG.error("Can not find declaration for descriptor " + memberDescriptor); - } - else { - DescriptorClassMember member = new DescriptorClassMember(declaration, memberDescriptor); - members.add(member); - } - } - return members; - } - - public static void generateMethods( - @NotNull final Editor editor, - @NotNull final JetClassOrObject classOrObject, - @NotNull final List selectedElements - ) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - JetClassBody body = classOrObject.getBody(); - if (body == null) { - JetPsiFactory psiFactory = JetPsiFactory(classOrObject); - classOrObject.add(psiFactory.createWhiteSpace()); - body = (JetClassBody) classOrObject.add(psiFactory.createEmptyClassBody()); - } - - PsiElement afterAnchor = findInsertAfterAnchor(editor, body); - - if (afterAnchor == null) return; - - PsiElement firstGenerated = null; - - List elementsToCompact = new ArrayList(); - for (JetElement element : generateOverridingMembers(selectedElements, classOrObject)) { - PsiElement added = body.addAfter(element, afterAnchor); - - if (firstGenerated == null) { - firstGenerated = added; - } - - afterAnchor = added; - elementsToCompact.add((JetElement) added); - } - - ShortenReferences.DEFAULT.process(elementsToCompact); - - if (firstGenerated == null) return; - - Project project = classOrObject.getProject(); - SmartPsiElementPointer pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(firstGenerated); - - PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); - - PsiElement element = pointer.getElement(); - if (element != null) { - QuickfixPackage.moveCaretIntoGeneratedElement(editor, element); - } - } - }); - } - - @Nullable - private static PsiElement findInsertAfterAnchor(Editor editor, final JetClassBody body) { - PsiElement afterAnchor = body.getLBrace(); - if (afterAnchor == null) return null; - - int offset = editor.getCaretModel().getOffset(); - PsiElement offsetCursorElement = PsiTreeUtil.findFirstParent( - body.getContainingFile().findElementAt(offset), - new Condition() { - @Override - public boolean value(PsiElement element) { - return element.getParent() == body; - } - }); - - if (offsetCursorElement instanceof PsiWhiteSpace) { - return removeAfterOffset(offset, (PsiWhiteSpace) offsetCursorElement); - } - - if (offsetCursorElement != null && offsetCursorElement != body.getRBrace()) { - return offsetCursorElement; - } - - return afterAnchor; - } - - private static PsiElement removeAfterOffset(int offset, PsiWhiteSpace whiteSpace) { - ASTNode spaceNode = whiteSpace.getNode(); - if (spaceNode.getTextRange().contains(offset)) { - String beforeWhiteSpaceText = spaceNode.getText().substring(0, offset - spaceNode.getStartOffset()); - if (!StringUtil.containsLineBreak(beforeWhiteSpaceText)) { - // Prevent insertion on same line - beforeWhiteSpaceText += "\n"; - } - - JetPsiFactory factory = JetPsiFactory(whiteSpace.getProject()); - - PsiElement insertAfter = whiteSpace.getPrevSibling(); - whiteSpace.delete(); - - PsiElement beforeSpace = factory.createWhiteSpace(beforeWhiteSpaceText); - insertAfter.getParent().addAfter(beforeSpace, insertAfter); - - return insertAfter.getNextSibling(); - } - - return whiteSpace; - } - - private static List generateOverridingMembers(List selectedElements, JetClassOrObject classOrObject) { - List overridingMembers = new ArrayList(); - for (DescriptorClassMember selectedElement : selectedElements) { - DeclarationDescriptor descriptor = selectedElement.getDescriptor(); - if (descriptor instanceof SimpleFunctionDescriptor) { - overridingMembers.add(overrideFunction(classOrObject, (SimpleFunctionDescriptor) descriptor)); - } - else if (descriptor instanceof PropertyDescriptor) { - overridingMembers.add(overrideProperty(classOrObject, (PropertyDescriptor) descriptor)); - } - } - return overridingMembers; - } - - @NotNull - private static JetElement overrideProperty(@NotNull JetClassOrObject classOrObject, @NotNull PropertyDescriptor descriptor) { - PropertyDescriptor newDescriptor = (PropertyDescriptor) descriptor.copy( - descriptor.getContainingDeclaration(), - Modality.OPEN, - descriptor.getVisibility(), - descriptor.getKind(), - /* copyOverrides = */ true); - newDescriptor.addOverriddenDescriptor(descriptor); - - StringBuilder body = new StringBuilder(); - body.append("\nget()"); - body.append(" = "); - body.append(generateUnsupportedOrSuperCall(classOrObject, descriptor)); - if (descriptor.isVar()) { - body.append("\nset(value) {}"); - } - return JetPsiFactory(classOrObject.getProject()).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body); - } - - @NotNull - private static JetNamedFunction overrideFunction(@NotNull JetClassOrObject classOrObject, @NotNull FunctionDescriptor descriptor) { - FunctionDescriptor newDescriptor = descriptor.copy( - descriptor.getContainingDeclaration(), - Modality.OPEN, - descriptor.getVisibility(), - descriptor.getKind(), - /* copyOverrides = */ true); - newDescriptor.addOverriddenDescriptor(descriptor); - - - JetType returnType = descriptor.getReturnType(); - KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); - - boolean returnsNotUnit = returnType != null && !builtIns.getUnitType().equals(returnType); - boolean isAbstract = descriptor.getModality() == Modality.ABSTRACT; - - String delegation = generateUnsupportedOrSuperCall(classOrObject, descriptor); - - String body = "{" + (returnsNotUnit && !isAbstract ? "return " : "") + delegation + "}"; - - return JetPsiFactory(classOrObject.getProject()).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body); - } - - private static String generateUnsupportedOrSuperCall(@NotNull JetClassOrObject classOrObject, @NotNull CallableMemberDescriptor descriptor) { - boolean isAbstract = descriptor.getModality() == Modality.ABSTRACT; - if (isAbstract) { - return "throw UnsupportedOperationException()"; - } - else { - StringBuilder builder = new StringBuilder(); - builder.append("super"); - if (classOrObject.getDelegationSpecifiers().size() > 1) { - builder.append("<").append(DescriptorRenderer.COMPACT.renderName(descriptor.getContainingDeclaration().getName())).append(">"); - } - builder.append(".").append(DescriptorRenderer.COMPACT.renderName(descriptor.getName())); - - if (descriptor instanceof FunctionDescriptor) { - builder.append("("); - boolean first = true; - for (ValueParameterDescriptor parameterDescriptor : descriptor.getValueParameters()) { - if (!first) { - builder.append(", "); - } - first = false; - builder.append(DescriptorRenderer.COMPACT.renderName(parameterDescriptor.getName())); - } - builder.append(")"); - } - - return builder.toString(); - } - } - - @NotNull - public Set collectMethodsToGenerate(@NotNull JetClassOrObject classOrObject) { - DeclarationDescriptor descriptor = ResolvePackage.resolveToDescriptor(classOrObject); - if (descriptor instanceof ClassDescriptor) { - return collectMethodsToGenerate((ClassDescriptor) descriptor); - } - return Collections.emptySet(); - } - - protected abstract Set collectMethodsToGenerate(@NotNull ClassDescriptor descriptor); - - private MemberChooser showOverrideImplementChooser( - Project project, - DescriptorClassMember[] members - ) { - MemberChooser chooser = new MemberChooser(members, true, true, project); - chooser.setTitle(getChooserTitle()); - chooser.show(); - if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; - return chooser; - } - - protected abstract String getChooserTitle(); - - @Override - public boolean isValidFor(Editor editor, PsiFile file) { - if (!(file instanceof JetFile)) { - return false; - } - PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); - JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); - return classOrObject != null; - } - - protected abstract String getNoMethodsFoundHint(); - - public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, boolean implementAll) { - PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); - JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); - - assert classOrObject != null; - - Set missingImplementations = collectMethodsToGenerate(classOrObject); - if (missingImplementations.isEmpty() && !implementAll) { - HintManager.getInstance().showErrorHint(editor, getNoMethodsFoundHint()); - return; - } - List members = membersFromDescriptors((JetFile) file, missingImplementations); - - List selectedElements; - if (implementAll) { - selectedElements = members; - } - else { - MemberChooser chooser = showOverrideImplementChooser( - project, - members.toArray(new DescriptorClassMember[members.size()])); - - if (chooser == null) { - return; - } - - selectedElements = chooser.getSelectedElements(); - if (selectedElements == null || selectedElements.isEmpty()) return; - } - - PsiDocumentManager.getInstance(project).commitAllDocuments(); - - generateMethods(editor, classOrObject, selectedElements); - } - - @Override - public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - invoke(project, editor, file, false); - } - - @Override - public boolean startInWriteAction() { - return false; - } -} diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.kt new file mode 100644 index 00000000000..25fb3ac0e8e --- /dev/null +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/codeInsight/OverrideImplementMethodsHandler.kt @@ -0,0 +1,311 @@ +/* + * 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.core.codeInsight + +import com.intellij.codeInsight.hint.HintManager +import com.intellij.ide.util.MemberChooser +import com.intellij.lang.ASTNode +import com.intellij.lang.LanguageCodeInsightActionHandler +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.util.Condition +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.* +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.quickfix.* +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.renderer.DescriptorRenderer +import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder +import org.jetbrains.kotlin.renderer.NameShortness +import org.jetbrains.kotlin.types.JetType + +import java.util.ArrayList +import java.util.Collections + +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType + +public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActionHandler { + + public fun collectMethodsToGenerate(classOrObject: JetClassOrObject): Set { + val descriptor = classOrObject.resolveToDescriptor() + if (descriptor is ClassDescriptor) { + return collectMethodsToGenerate(descriptor) + } + return emptySet() + } + + protected abstract fun collectMethodsToGenerate(descriptor: ClassDescriptor): Set + + private fun showOverrideImplementChooser(project: Project, members: Array): MemberChooser? { + val chooser = MemberChooser(members, true, true, project) + chooser.setTitle(getChooserTitle()) + chooser.show() + if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null + return chooser + } + + protected abstract fun getChooserTitle(): String + + override fun isValidFor(editor: Editor, file: PsiFile): Boolean { + if (file !is JetFile) { + return false + } + val elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()) + val classOrObject = elementAtCaret?.getNonStrictParentOfType() + return classOrObject != null + } + + protected abstract fun getNoMethodsFoundHint(): String + + public fun invoke(project: Project, editor: Editor, file: PsiFile, implementAll: Boolean) { + val elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()) + val classOrObject = elementAtCaret?.getNonStrictParentOfType()!! + + val missingImplementations = collectMethodsToGenerate(classOrObject) + if (missingImplementations.isEmpty() && !implementAll) { + HintManager.getInstance().showErrorHint(editor, getNoMethodsFoundHint()) + return + } + val members = membersFromDescriptors(file as JetFile, missingImplementations) + + val selectedElements: List? + if (implementAll) { + selectedElements = members + } + else { + val chooser = showOverrideImplementChooser(project, members.toTypedArray()) + + if (chooser == null) { + return + } + + selectedElements = chooser.getSelectedElements() + if (selectedElements == null || selectedElements.isEmpty()) return + } + + PsiDocumentManager.getInstance(project).commitAllDocuments() + + generateMethods(editor, classOrObject, selectedElements) + } + + override fun invoke(project: Project, editor: Editor, file: PsiFile) = invoke(project, editor, file, false) + + override fun startInWriteAction(): Boolean = false + + companion object { + private val OVERRIDE_RENDERER = DescriptorRendererBuilder() + .setRenderDefaultValues(false) + .setModifiers(DescriptorRenderer.Modifier.OVERRIDE) + .setWithDefinedIn(false) + .setNameShortness(NameShortness.SOURCE_CODE_QUALIFIED) + .setOverrideRenderingPolicy(DescriptorRenderer.OverrideRenderingPolicy.RENDER_OVERRIDE) + .setUnitReturnType(false) + .setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES) + .build() + + private val LOG = Logger.getInstance(javaClass().getCanonicalName()) + + public fun membersFromDescriptors(file: JetFile, missingImplementations: Iterable): List { + val members = ArrayList() + for (memberDescriptor in missingImplementations) { + val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.getProject(), memberDescriptor) + if (declaration == null) { + LOG.error("Can not find declaration for descriptor " + memberDescriptor) + } + else { + val member = DescriptorClassMember(declaration, memberDescriptor) + members.add(member) + } + } + return members + } + + public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: List) { + runWriteAction { + var body = classOrObject.getBody() + if (body == null) { + val psiFactory = JetPsiFactory(classOrObject) + classOrObject.add(psiFactory.createWhiteSpace()) + body = classOrObject.add(psiFactory.createEmptyClassBody()) as JetClassBody + } + + var afterAnchor = findInsertAfterAnchor(editor, body) + + if (afterAnchor == null) return@runWriteAction + + var firstGenerated: PsiElement? = null + + val elementsToCompact = ArrayList() + for (element in generateOverridingMembers(selectedElements, classOrObject)) { + val added = body!!.addAfter(element, afterAnchor) + + if (firstGenerated == null) { + firstGenerated = added + } + + afterAnchor = added + elementsToCompact.add(added as JetElement) + } + + ShortenReferences.DEFAULT.process(elementsToCompact) + + if (firstGenerated == null) return@runWriteAction + + val project = classOrObject.getProject() + val pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(firstGenerated) + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()) + + val element = pointer.getElement() + if (element != null) { + moveCaretIntoGeneratedElement(editor, element) + } + + } + } + + private fun findInsertAfterAnchor(editor: Editor, body: JetClassBody): PsiElement? { + val afterAnchor = body.getLBrace() + if (afterAnchor == null) return null + + val offset = editor.getCaretModel().getOffset() + val offsetCursorElement = PsiTreeUtil.findFirstParent(body.getContainingFile().findElementAt(offset)) { + it.getParent() == body + } + + if (offsetCursorElement is PsiWhiteSpace) { + return removeAfterOffset(offset, offsetCursorElement) + } + + if (offsetCursorElement != null && offsetCursorElement != body.getRBrace()) { + return offsetCursorElement + } + + return afterAnchor + } + + private fun removeAfterOffset(offset: Int, whiteSpace: PsiWhiteSpace): PsiElement { + val spaceNode = whiteSpace.getNode() + if (spaceNode.getTextRange().contains(offset)) { + var beforeWhiteSpaceText = spaceNode.getText().substring(0, offset - spaceNode.getStartOffset()) + if (!StringUtil.containsLineBreak(beforeWhiteSpaceText)) { + // Prevent insertion on same line + beforeWhiteSpaceText += "\n" + } + + val factory = JetPsiFactory(whiteSpace.getProject()) + + val insertAfter = whiteSpace.getPrevSibling() + whiteSpace.delete() + + val beforeSpace = factory.createWhiteSpace(beforeWhiteSpaceText) + insertAfter.getParent().addAfter(beforeSpace, insertAfter) + + return insertAfter.getNextSibling() + } + + return whiteSpace + } + + private fun generateOverridingMembers(selectedElements: List, + classOrObject: JetClassOrObject): List { + val overridingMembers = ArrayList() + for (selectedElement in selectedElements) { + val descriptor = selectedElement.getDescriptor() + if (descriptor is SimpleFunctionDescriptor) { + overridingMembers.add(overrideFunction(classOrObject, descriptor)) + } + else if (descriptor is PropertyDescriptor) { + overridingMembers.add(overrideProperty(classOrObject, descriptor)) + } + } + return overridingMembers + } + + private fun overrideProperty(classOrObject: JetClassOrObject, descriptor: PropertyDescriptor): JetElement { + val newDescriptor = descriptor.copy(descriptor.getContainingDeclaration(), Modality.OPEN, descriptor.getVisibility(), + descriptor.getKind(), /* copyOverrides = */ true) as PropertyDescriptor + newDescriptor.addOverriddenDescriptor(descriptor) + + val body = StringBuilder() + body.append("\nget()") + body.append(" = ") + body.append(generateUnsupportedOrSuperCall(classOrObject, descriptor)) + if (descriptor.isVar()) { + body.append("\nset(value) {}") + } + return JetPsiFactory(classOrObject.getProject()).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body) + } + + private fun overrideFunction(classOrObject: JetClassOrObject, descriptor: FunctionDescriptor): JetNamedFunction { + val newDescriptor = descriptor.copy(descriptor.getContainingDeclaration(), Modality.OPEN, descriptor.getVisibility(), + descriptor.getKind(), /* copyOverrides = */ true) + newDescriptor.addOverriddenDescriptor(descriptor) + + val returnType = descriptor.getReturnType() + val builtIns = KotlinBuiltIns.getInstance() + + val returnsNotUnit = returnType != null && builtIns.getUnitType() != returnType + val isAbstract = descriptor.getModality() == Modality.ABSTRACT + + val delegation = generateUnsupportedOrSuperCall(classOrObject, descriptor) + + val body = "{" + (if (returnsNotUnit && !isAbstract) "return " else "") + delegation + "}" + + return JetPsiFactory(classOrObject.getProject()).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body) + } + + private fun generateUnsupportedOrSuperCall(classOrObject: JetClassOrObject, descriptor: CallableMemberDescriptor): String { + val isAbstract = descriptor.getModality() == Modality.ABSTRACT + if (isAbstract) { + return "throw UnsupportedOperationException()" + } + else { + val builder = StringBuilder() + builder.append("super") + if (classOrObject.getDelegationSpecifiers().size() > 1) { + builder.append("<").append(descriptor.getContainingDeclaration().escapedName()).append(">") + } + builder.append(".").append(descriptor.escapedName()) + + if (descriptor is FunctionDescriptor) { + val paramTexts = descriptor.getValueParameters().map { + val renderedName = it.escapedName() + if (it.getVarargElementType() != null) "*$renderedName" else renderedName + } + paramTexts.joinTo(builder, prefix="(", postfix=")") + } + + return builder.toString() + } + } + + fun DeclarationDescriptor.escapedName() = + DescriptorRenderer.COMPACT.renderName(getName()) + } +} diff --git a/idea/testData/codeInsight/overrideImplement/varArgs.kt b/idea/testData/codeInsight/overrideImplement/varArgs.kt new file mode 100644 index 00000000000..10309a7a438 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/varArgs.kt @@ -0,0 +1,7 @@ +open class S { + open fun s(vararg v: Int) {} +} + +class D : S() { + +} diff --git a/idea/testData/codeInsight/overrideImplement/varArgs.kt.after b/idea/testData/codeInsight/overrideImplement/varArgs.kt.after new file mode 100644 index 00000000000..fd52d2bf38f --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/varArgs.kt.after @@ -0,0 +1,9 @@ +open class S { + open fun s(vararg v: Int) {} +} + +class D : S() { + override fun s(vararg v: Int) { + super.s(*v) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.java index c24b23f285e..fb03276a079 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOverrideImplementTest.java @@ -164,7 +164,7 @@ public abstract class AbstractOverrideImplementTest extends JetLightCodeInsightF } performGenerateCommand(classOrObject, - OverrideImplementMethodsHandler.membersFromDescriptors(jetFile, Collections.singletonList(singleToOverride))); + OverrideImplementMethodsHandler.Companion.membersFromDescriptors(jetFile, Collections.singletonList(singleToOverride))); } private void doMultiOverrideImplement(OverrideImplementMethodsHandler handler) { @@ -183,7 +183,7 @@ public abstract class AbstractOverrideImplementTest extends JetLightCodeInsightF } }); - performGenerateCommand(classOrObject, OverrideImplementMethodsHandler.membersFromDescriptors(jetFile, descriptorsList)); + performGenerateCommand(classOrObject, OverrideImplementMethodsHandler.Companion.membersFromDescriptors(jetFile, descriptorsList)); } private void generateImplementation(@NotNull final PsiMethod method) { @@ -210,7 +210,7 @@ public abstract class AbstractOverrideImplementTest extends JetLightCodeInsightF new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) { @Override protected void run(@NotNull Result result) throws Throwable { - OverrideImplementMethodsHandler.generateMethods(myFixture.getEditor(), classOrObject, descriptorsToGenerate); + OverrideImplementMethodsHandler.Companion.generateMethods(myFixture.getEditor(), classOrObject, descriptorsToGenerate); } }.performCommand(); } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.java index c8041199894..ff0264a7781 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OverrideImplementTest.java @@ -205,4 +205,8 @@ public final class OverrideImplementTest extends AbstractOverrideImplementTest { public void testEscapeIdentifiers() { doOverrideFileTest(); } + + public void testVarArgs() { + doOverrideFileTest(); + } }