diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java index 46f991b81d0..853d94cc51c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java @@ -68,8 +68,15 @@ public class JetClassBody extends JetElement { return (JetClassObject) findChildByType(JetNodeTypes.CLASS_OBJECT); } + @Nullable public PsiElement getRBrace() { final ASTNode[] children = getNode().getChildren(TokenSet.create(JetTokens.RBRACE)); return children.length == 1 ? children[0].getPsi() : null; } + + @Nullable + public PsiElement getLBrace() { + final ASTNode[] children = getNode().getChildren(TokenSet.create(JetTokens.LBRACE)); + return children.length == 1 ? children[0].getPsi() : null; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java index e291fd34a7f..ce6ba4c4fc5 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java @@ -19,15 +19,16 @@ package org.jetbrains.jet.plugin.codeInsight; import com.intellij.codeInsight.hint.HintManager; import com.intellij.ide.util.MemberChooser; import com.intellij.lang.LanguageCodeInsightActionHandler; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.application.ApplicationManager; 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.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -54,34 +55,71 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns } public Set collectMethodsToGenerate(JetClassOrObject classOrObject) { - BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) classOrObject.getContainingFile()); + BindingContext bindingContext = + WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile)classOrObject.getContainingFile()); final DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject); if (descriptor instanceof MutableClassDescriptor) { - return collectMethodsToGenerate((MutableClassDescriptor) descriptor); + return collectMethodsToGenerate((MutableClassDescriptor)descriptor); } return Collections.emptySet(); } protected abstract Set collectMethodsToGenerate(MutableClassDescriptor descriptor); - public static void generateMethods(Project project, Editor editor, JetClassOrObject classOrObject, List selectedElements) { + public static void generateMethods(Project project, + Editor editor, + JetClassOrObject classOrObject, + List selectedElements) { final JetClassBody body = classOrObject.getBody(); if (body == null) { return; } - for (DescriptorClassMember selectedElement : selectedElements) { - final DeclarationDescriptor descriptor = selectedElement.getDescriptor(); - JetFile containingFile = (JetFile) classOrObject.getContainingFile(); - if (descriptor instanceof SimpleFunctionDescriptor) { - JetElement target = overrideFunction(project, containingFile, (SimpleFunctionDescriptor) descriptor); - body.addBefore(target, body.getRBrace()); - } - else if (descriptor instanceof PropertyDescriptor) { - JetElement target = overrideProperty(project, containingFile, (PropertyDescriptor) descriptor); - body.addBefore(target, body.getRBrace()); - } - } + // NOTE + TODO: If you try to cache findInsertBeforeAnchor element, there will be failed assertion + // "PSI/document inconsistency before reparse: file=" from DocumentCommitThread after inserting two overriding + // with the caret right before existing function start. + + PsiElement afterAnchor = findInsertAfterAnchor(editor, body); + + if (afterAnchor == null) { + return; + } + + for (DescriptorClassMember selectedElement : selectedElements) { + final DeclarationDescriptor descriptor = selectedElement.getDescriptor(); + JetFile containingFile = (JetFile)classOrObject.getContainingFile(); + if (descriptor instanceof SimpleFunctionDescriptor) { + JetElement target = overrideFunction(project, containingFile, (SimpleFunctionDescriptor)descriptor); + afterAnchor = body.addAfter(target, afterAnchor); + } + else if (descriptor instanceof PropertyDescriptor) { + JetElement target = overrideProperty(project, containingFile, (PropertyDescriptor)descriptor); + afterAnchor = body.addBefore(target, afterAnchor); + } + } + } + + @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 != null) { + afterAnchor = offsetCursorElement; + } + + return afterAnchor; } private static JetElement overrideProperty(Project project, JetFile file, PropertyDescriptor descriptor) { @@ -149,7 +187,7 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns } private MemberChooser showOverrideImplementChooser(Project project, - DescriptorClassMember[] members) { + DescriptorClassMember[] members) { final MemberChooser chooser = new MemberChooser(members, true, true, project); chooser.setTitle(getChooserTitle()); chooser.show(); @@ -185,9 +223,12 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns final List selectedElements; if (implementAll) { selectedElements = members; - } else { + } + else { final MemberChooser chooser = showOverrideImplementChooser(project, - members.toArray(new DescriptorClassMember[members.size()])); + members.toArray( + new DescriptorClassMember[members + .size()])); if (chooser == null) { return; } @@ -196,12 +237,12 @@ public abstract class OverrideImplementMethodsHandler implements LanguageCodeIns if (selectedElements == null || selectedElements.isEmpty()) return; } - new WriteCommandAction(project, file) { - protected void run(final Result result) throws Throwable { - generateMethods(project, editor, classOrObject, selectedElements); - } - }.execute(); - + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + generateMethods(project, editor, classOrObject, selectedElements); + } + }); } @Override diff --git a/idea/testData/codeInsight/overrideImplement/property.kt.after b/idea/testData/codeInsight/overrideImplement/property.kt.after index 782da651f85..d12fd5d6f86 100644 --- a/idea/testData/codeInsight/overrideImplement/property.kt.after +++ b/idea/testData/codeInsight/overrideImplement/property.kt.after @@ -3,6 +3,6 @@ trait T { } class GC() : T { - override val v : Int = 0 + } diff --git a/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt b/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt new file mode 100644 index 00000000000..25bc81d3009 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt @@ -0,0 +1,15 @@ +trait Test { + public open fun test() + protected open val testProp : Int +} + +class SomeTest : Test { + val hello = 12 + + /** + * test + */ + fun some() { + + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt.after b/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt.after new file mode 100644 index 00000000000..af65b63163a --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/respectCaretPosition.kt.after @@ -0,0 +1,19 @@ +trait Test { + public open fun test() + protected open val testProp : Int +} + +class SomeTest : Test { + val hello = 12 + override val testProp : Int = 0 + override fun test() { + throw UnsupportedOperationException() + } + + /** + * test + */ + fun some() { + + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java b/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java index 673f3e34057..43d3e4ad078 100644 --- a/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java @@ -69,14 +69,18 @@ public class OverrideImplementTest extends LightCodeInsightFixtureTestCase { doFileTest(); } - public void testTraitNullableFunction() { - doFileTest(); + public void testRespectCaretPosition() { + doMultiFileTest(); } public void testGenerateMulti() { doMultiFileTest(); } + public void testTraitNullableFunction() { + doFileTest(); + } + private void doFileTest() { myFixture.configureByFile(getTestName(true) + ".kt"); doImplement(); @@ -100,7 +104,7 @@ public class OverrideImplementTest extends LightCodeInsightFixtureTestCase { final PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset()); final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); final Set descriptors = new ImplementMethodsHandler().collectMethodsToGenerate(classOrObject); - assertEquals(1, descriptors.size()); + assertEquals("Invalid number of available descriptors for override", 1, descriptors.size()); new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) { @Override protected void run(Result result) throws Throwable {