diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index bdc0e27dd101..0dd46fd058a7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -509,6 +509,12 @@ public class JavaCompletionContributor extends CompletionContributor { return; } + final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false); + if (ref != null && !(ref instanceof PsiReferenceExpression)) { + context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";"); + return; + } + final PsiElement element = file.findElementAt(context.getStartOffset()); if (psiElement().inside(PsiAnnotation.class).accepts(element)) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 9a8a099f3108..15f529c6979f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -680,8 +680,8 @@ public class JavaCompletionUtil { final Document document = context.getEditor().getDocument(); PsiDocumentManager.getInstance(project).commitDocument(document); final PsiFile file = context.getFile(); - final PsiReferenceExpression ref = - PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiReferenceExpression.class, false); + final PsiJavaCodeReferenceElement ref = + PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false); if (ref != null) { final PsiElement qualifier = ref.getQualifier(); if (qualifier != null) { @@ -1001,11 +1001,11 @@ public class JavaCompletionUtil { } public static boolean hasAccessibleInnerClass(@NotNull PsiClass psiClass, @NotNull PsiElement position) { - final PsiClass[] inners = psiClass.getAllInnerClasses(); + final PsiClass[] inners = psiClass.getInnerClasses(); if (inners.length > 0) { PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(position.getProject()).getResolveHelper(); for (PsiClass inner : inners) { - if (resolveHelper.isAccessible(inner, position, null)) { + if (inner.hasModifierProperty(PsiModifier.STATIC) && resolveHelper.isAccessible(inner, position, null)) { return true; } } diff --git a/java/java-impl/src/com/intellij/psi/util/proximity/KnownPackageWeigher.java b/java/java-impl/src/com/intellij/psi/util/proximity/KnownElementWeigher.java similarity index 69% rename from java/java-impl/src/com/intellij/psi/util/proximity/KnownPackageWeigher.java rename to java/java-impl/src/com/intellij/psi/util/proximity/KnownElementWeigher.java index fdc2e7dd629a..f60c6478dc0a 100644 --- a/java/java-impl/src/com/intellij/psi/util/proximity/KnownPackageWeigher.java +++ b/java/java-impl/src/com/intellij/psi/util/proximity/KnownElementWeigher.java @@ -15,8 +15,10 @@ */ package com.intellij.psi.util.proximity; +import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; import com.intellij.psi.util.ProximityLocation; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -24,7 +26,7 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public class KnownPackageWeigher extends ProximityWeigher { +public class KnownElementWeigher extends ProximityWeigher { public Comparable weigh(@NotNull final PsiElement element, @NotNull final ProximityLocation location) { if (element instanceof PsiClass) { @@ -34,6 +36,15 @@ public class KnownPackageWeigher extends ProximityWeigher { if (qname.startsWith("javax.")) return 1; } } + if (element instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)element; + if ("finalize".equals(method.getName()) || "registerNatives".equals(method.getName())) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) { + return -1; + } + } + } return 0; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab.java index 723d66f49d22..2c1cada0ad5c 100644 --- a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab.java +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab.java @@ -10,7 +10,7 @@ class Xxx { private Xxx(String x) { } - class Yyy { + static class Yyy { } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab_after.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab_after.java index 28d5cebdbd75..240707bdbf6d 100644 --- a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab_after.java +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInnersTab_after.java @@ -10,7 +10,7 @@ class Xxx { private Xxx(String x) { } - class Yyy { + static class Yyy { } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner.java new file mode 100644 index 000000000000..ba8e6d1f8dec --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner.java @@ -0,0 +1,11 @@ +class Foo { + { + Zzoo l = new Zz + } +} + +class Zzoo { + void run(); + + class Impl implements Zzoo {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner_after.java b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner_after.java new file mode 100644 index 000000000000..2ee90dfbd4a7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/ClassNameWithInstanceInner_after.java @@ -0,0 +1,11 @@ +class Foo { + { + Zzoo l = new Zzoo() + } +} + +class Zzoo { + void run(); + + class Impl implements Zzoo {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt.java b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt.java new file mode 100644 index 000000000000..d784b3457ee3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2011 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. + */ +import javax.swing.*; +import java.awt.*; + +public class Bar { + + static class CellWrapper { + public boolean isSeparator() { + return true; + } + } + + static class MyRenderer extends DefaultListCellRenderer { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean s, boolean focus) { + assert value instanceof CellWrapper; + + value.is + + } + } + +} + diff --git a/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt_after.java b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt_after.java new file mode 100644 index 000000000000..9095c2be7d6b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingBeforeLt_after.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2011 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. + */ +import javax.swing.*; +import java.awt.*; + +public class Bar { + + static class CellWrapper { + public boolean isSeparator() { + return true; + } + } + + static class MyRenderer extends DefaultListCellRenderer { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean s, boolean focus) { + assert value instanceof CellWrapper; + + ((CellWrapper) value).isSeparator() + + } + } + +} + diff --git a/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments.java b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments.java new file mode 100644 index 000000000000..e9d44984664e --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments.java @@ -0,0 +1,24 @@ +import javax.swing.*; +import java.awt.*; + +public class Bar { + + static class CellWrapper { + public boolean isSeparator() { + return true; + } + } + + static class MyRenderer extends DefaultListCellRenderer { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean s, boolean focus) { + assert value instanceof CellWrapper; + + value.is + + mySelected = isSelected; + + } + } + +} + diff --git a/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments_after.java b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments_after.java new file mode 100644 index 000000000000..3055fdb246f3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/QualifierCastingWithUnknownAssignments_after.java @@ -0,0 +1,24 @@ +import javax.swing.*; +import java.awt.*; + +public class Bar { + + static class CellWrapper { + public boolean isSeparator() { + return true; + } + } + + static class MyRenderer extends DefaultListCellRenderer { + public Component getListCellRendererComponent(JList list, Object value, int index, boolean s, boolean focus) { + assert value instanceof CellWrapper; + + ((CellWrapper) value).isSeparator() + + mySelected = isSelected; + + } + } + +} + diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferFinalize.java b/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferFinalize.java new file mode 100644 index 000000000000..aaee7b707995 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/DispreferFinalize.java @@ -0,0 +1,7 @@ +public class Bar { + + { + finax + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 868b6b9c0c0f..6bdd6f45061b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -17,8 +17,6 @@ package com.intellij.codeInsight.completion import com.intellij.codeInsight.lookup.Lookup import com.intellij.openapi.actionSystem.IdeActions -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.command.CommandProcessor /** @@ -252,13 +250,13 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { } """) type 'ite' - ApplicationManager.application.invokeAndWait({ - myFixture.type 'r' - lookup.markReused() - lookup.currentItem = lookup.items[0] - CommandProcessor.instance.executeCommand project, ({lookup.finishLookup Lookup.NORMAL_SELECT_CHAR} as Runnable), null, null + edt { + myFixture.type 'r' + lookup.markReused() + lookup.currentItem = lookup.items[0] + CommandProcessor.instance.executeCommand project, ({lookup.finishLookup Lookup.NORMAL_SELECT_CHAR} as Runnable), null, null - } as Runnable, ModalityState.NON_MODAL) + } myFixture.checkResult """ class A { Iterable iterable; { iterable } @@ -273,14 +271,13 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { } """) type 'ite' - ApplicationManager.application.invokeAndWait({ - myFixture.type 'r' - lookup.markReused() - myFixture.type '\b\b' - lookup.currentItem = lookup.items[0] - CommandProcessor.instance.executeCommand project, ({lookup.finishLookup Lookup.NORMAL_SELECT_CHAR} as Runnable), null, null - - } as Runnable, ModalityState.NON_MODAL) + edt { + myFixture.type 'r' + lookup.markReused() + myFixture.type '\b\b' + lookup.currentItem = lookup.items[0] + CommandProcessor.instance.executeCommand project, ({lookup.finishLookup Lookup.NORMAL_SELECT_CHAR} as Runnable), null, null + } myFixture.checkResult """ class A { Iterable iterable; { iterable } @@ -295,10 +292,7 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { } """) type 'th' - ApplicationManager.application.invokeAndWait({ - myFixture.type 'r' - myFixture.type '\t' - } as Runnable, ModalityState.NON_MODAL) + edt { myFixture.type 'r\t'} myFixture.checkResult """ class A { { throw new } @@ -306,4 +300,35 @@ class JavaAutoPopupTest extends CompletionAutoPopupTestCase { """ } + public void testTwoQuickRestartsAfterHiding() { + myFixture.configureByText("a.java", """ + class A { + { } + } + """) + edt { myFixture.type 'A' } + joinAlarm() // completion started + edt { assert lookup; myFixture.type 'IO' } + joinAlarm() + joinAlarm() + joinCompletion() + assert lookup + assert 'ArrayIndexOutOfBoundsException' in myFixture.lookupElementStrings + } + + public void testTypingDuringExplicitCompletion() { + myFixture.configureByText("a.java", """ + class A { + { Runnable r = new } + } + """) + myFixture.complete CompletionType.SMART + edt { myFixture.type 'Thr' } + joinCompletion() + assert lookup + assert 'Thread' in myFixture.lookupElementStrings + } + + + } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.java index 2c30ef817fa7..78e138459167 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.java @@ -138,7 +138,6 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase { assertEquals(1, ((PsiMethod)myItems[1].getObject()).getParameterList().getParametersCount()); assertEquals(2, ((PsiMethod)myItems[2].getObject()).getParameterList().getParametersCount()); } - public void testStatsForClassNameInExpression() throws Throwable { final VirtualFile foo = getSourceRoot().createChildDirectory(this, "foo"); VfsUtil.saveText(foo.createChildData(this, "FooBar.java"), "package foo; public interface FooBar {}"); @@ -149,4 +148,8 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase { assertPreferredItems(0, "FooBee", "FooBar"); } + public void testDispreferFinalize() throws Throwable { + checkPreferredItems(0, "final", "finalize"); + } + } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 5f3e4c01b398..0b9e799a9157 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -664,6 +664,8 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { public void testClassNameWithInner() throws Throwable { doTest() } + public void testClassNameWithInstanceInner() throws Throwable { doTest('\n') } + public void testDoubleFalse() throws Throwable { configureByFile(getTestName(false) + ".java"); assertStringItems("false", "finalize"); @@ -785,6 +787,8 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { public void testCastTooComplexInstanceofedQualifier() throws Throwable { doAntiTest(); } public void testDontCastInstanceofedQualifier() throws Throwable { doTest(); } + public void testQualifierCastingWithUnknownAssignments() throws Throwable { doTest(); } + public void testQualifierCastingBeforeLt() throws Throwable { doTest(); } public void testWildcardsInLookup() throws Exception { configure() diff --git a/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy b/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy index eec5c26704b2..42b0ce7f8a65 100644 --- a/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy +++ b/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy @@ -15,11 +15,11 @@ */ package com.intellij.codeInsight.completion +import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler import com.intellij.codeInsight.lookup.LookupManager import com.intellij.codeInsight.lookup.impl.LookupImpl -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.ModalityState +import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.util.ui.UIUtil @@ -28,13 +28,7 @@ import com.intellij.util.ui.UIUtil */ abstract class CompletionAutoPopupTestCase extends LightCodeInsightFixtureTestCase { @Override protected void setUp() { - UIUtil.invokeAndWaitIfNeeded(new Runnable(){ - @Override - void run() { - superSetUp() - } - - }) + edt { superSetUp() } CompletionAutoPopupHandler.ourTestingAutopopup = true } void superSetUp() { @@ -46,28 +40,43 @@ abstract class CompletionAutoPopupTestCase extends LightCodeInsightFixtureTestCa @Override protected void tearDown() { CompletionAutoPopupHandler.ourTestingAutopopup = false - UIUtil.invokeAndWaitIfNeeded(new Runnable(){ - @Override - void run() { - superTearDown() - } - - }) + edt { superTearDown() } } protected void doHighlighting() { - UIUtil.invokeAndWaitIfNeeded({ myFixture.doHighlighting() } as Runnable) + edt { myFixture.doHighlighting() } } void type(String s) { for (i in 0.. data = startCompletionThread(parameters, indicator, initContext); - if (!invokedExplicitly && (!ApplicationManager.getApplication().isUnitTestMode() || CompletionAutoPopupHandler.ourTestingAutopopup)) { + if ((!invokedExplicitly && !ApplicationManager.getApplication().isUnitTestMode()) || CompletionAutoPopupHandler.ourTestingAutopopup) { indicator.notifyBackgrounded(); return; } @@ -368,7 +379,7 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { } }; - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup) { computeRunnable.run(); } else { ApplicationManager.getApplication().executeOnPooledThread(computeRunnable); @@ -467,12 +478,14 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { final LookupElement[] items) { if (items.length == 0) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); - handleEmptyLookup(indicator.getProject(), indicator.getEditor(), indicator.getParameters(), indicator); + CompletionServiceImpl.setCompletionPhase( + handleEmptyLookup(indicator.getProject(), indicator.getEditor(), indicator.getParameters(), indicator)); return; } final AutoCompletionDecision decision = shouldAutoComplete(indicator, items); if (decision == AutoCompletionDecision.SHOW_LOOKUP) { + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated()); indicator.getLookup().setCalculating(false); indicator.showLookup(); if (isAutocompleteCommonPrefixOnInvocation() && items.length > 1) { @@ -488,10 +501,10 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { handleSingleItem(offset2, indicator, items, item.getLookupString(), item); // the insert handler may have started a live template with completion - if (CompletionService.getCompletionService().getCurrentCompletion() == null) { - indicator.liveAfterDeath(null); - } else { - LOG.assertTrue(!indicator.isZombie(), indicator); + if (CompletionService.getCompletionService().getCurrentCompletion() == null && + !ApplicationManager.getApplication().isUnitTestMode()) { + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.InsertedSingleItem(indicator)); + assert indicator.getCompletionState().isWaitingAfterAutoInsertion(); } } } @@ -591,22 +604,29 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { return invokedExplicitly && CodeInsightSettings.getInstance().AUTOCOMPLETE_COMMON_PREFIX; } - protected void handleEmptyLookup(Project project, Editor editor, final CompletionParameters parameters, final CompletionProgressIndicator indicator) { - if (ApplicationManager.getApplication().isUnitTestMode()) return; + protected CompletionPhase handleEmptyLookup(Project project, Editor editor, final CompletionParameters parameters, final CompletionProgressIndicator indicator) { + if (ApplicationManager.getApplication().isUnitTestMode()) return CompletionPhase.NoCompletion; if (!invokedExplicitly) { - return; + return CompletionPhase.NoCompletion; } + indicator.assertDisposed(); + assert !indicator.isAutopopupCompletion(); + final CompletionPhase[] result = {CompletionPhase.NoCompletion}; for (final CompletionContributor contributor : CompletionContributor.forParameters(parameters)) { final String text = contributor.handleEmptyLookup(parameters, editor); if (StringUtil.isNotEmpty(text)) { final EditorHintListener listener = new EditorHintListener() { public void hintShown(final Project project, final LightweightHint hint, final int flags) { - indicator.liveAfterDeath(hint); + if (!indicator.areModifiersChanged()) { + result[0] = new CompletionPhase.NoSuggestionsHint(hint, indicator); + CompletionServiceImpl.setCompletionPhase(result[0]); + } } }; final MessageBusConnection connection = project.getMessageBus().connect(); connection.subscribe(EditorHintListener.TOPIC, listener); + assert text != null; HintManager.getInstance().showErrorHint(editor, text); connection.disconnect(); break; @@ -616,32 +636,36 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { if (codeAnalyzer != null) { codeAnalyzer.updateVisibleHighlighters(editor); } + return result[0]; } - private static void lookupItemSelected(final CompletionProgressIndicator context, @NotNull final LookupElement item, final char completionChar, + private static void lookupItemSelected(final CompletionProgressIndicator indicator, @NotNull final LookupElement item, final char completionChar, final List items) { - if (context.getHandler().autopopup) { + if (indicator.getHandler().autopopup) { FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_BASIC); } - final Editor editor = context.getEditor(); - final PsiFile file = context.getParameters().getOriginalFile(); - final InsertionContext context1 = new InsertionContext(context.getOffsetMap(), completionChar, items.toArray(new LookupElement[items.size()]), file, editor); + final Editor editor = indicator.getEditor(); + final PsiFile file = indicator.getParameters().getOriginalFile(); + final InsertionContext context = new InsertionContext(indicator.getOffsetMap(), completionChar, items.toArray(new LookupElement[items.size()]), file, editor); ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { - final int idEndOffset = context.getIdentifierEndOffset(); - if (idEndOffset != context.getSelectionEndOffset() && CompletionUtil.isOverwrite(item, completionChar)) { - editor.getDocument().deleteString(context.getSelectionEndOffset(), idEndOffset); + final int idEndOffset = indicator.getIdentifierEndOffset(); + if (idEndOffset != indicator.getSelectionEndOffset() && CompletionUtil.isOverwrite(item, completionChar)) { + editor.getDocument().deleteString(indicator.getSelectionEndOffset(), idEndOffset); } - PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments(); - item.handleInsert(context1); - PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(); + assert context.getStartOffset() >= 0 : "stale startOffset"; + assert context.getTailOffset() >= 0 : "stale tailOffset"; + + PsiDocumentManager.getInstance(indicator.getProject()).commitAllDocuments(); + item.handleInsert(context); + PostprocessReformattingAspect.getInstance(indicator.getProject()).doPostponedFormatting(); - final int tailOffset = context1.getTailOffset(); + final int tailOffset = context.getTailOffset(); if (tailOffset >= 0) { - if (context1.shouldAddCompletionChar() && + if (context.shouldAddCompletionChar() && completionChar != Lookup.AUTO_INSERT_SELECT_CHAR && completionChar != Lookup.REPLACE_SELECT_CHAR && completionChar != Lookup.NORMAL_SELECT_CHAR && completionChar != Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { TailType.insertChar(editor, tailOffset, completionChar); @@ -653,11 +677,11 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } }); - final Runnable runnable = context1.getLaterRunnable(); + final Runnable runnable = context.getLaterRunnable(); if (runnable != null) { final Runnable runnable1 = new Runnable() { public void run() { - final Project project = context1.getProject(); + final Project project = context.getProject(); if (project.isDisposed()) return; runnable.run(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java new file mode 100644 index 000000000000..3f2432e9bb51 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.completion; + +import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.CaretModel; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.SelectionModel; +import com.intellij.openapi.editor.event.*; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.HintListener; +import com.intellij.ui.LightweightHint; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EventObject; + +/** + * @author peter + */ +public abstract class CompletionPhase implements Disposable { + public static final CompletionPhase NoCompletion = new CompletionPhase() {}; + + @Override + public void dispose() { + } + + public static class AutoPopupAlarm extends CompletionPhase {} + public static class Synchronous extends CompletionPhase {} + public static class BgCalculation extends CompletionPhase {} + public static class ItemsCalculated extends CompletionPhase {} + public static class Restarted extends CompletionPhase {} + + public static class ZombiePhase extends CompletionPhase { + public final CompletionProgressIndicator indicator; + + protected ZombiePhase(@Nullable final LightweightHint hint, final CompletionProgressIndicator indicator) { + this.indicator = indicator; + @NotNull Editor editor = indicator.getEditor(); + final HintListener hintListener = new HintListener() { + public void hintHidden(final EventObject event) { + CompletionServiceImpl.setCompletionPhase(NoCompletion); + } + }; + final DocumentAdapter documentListener = new DocumentAdapter() { + @Override + public void beforeDocumentChange(DocumentEvent e) { + CompletionServiceImpl.setCompletionPhase(NoCompletion); + } + }; + final SelectionListener selectionListener = new SelectionListener() { + public void selectionChanged(SelectionEvent e) { + CompletionServiceImpl.setCompletionPhase(NoCompletion); + } + }; + final CaretListener caretListener = new CaretListener() { + public void caretPositionChanged(CaretEvent e) { + CompletionServiceImpl.setCompletionPhase(NoCompletion); + } + }; + + final Document document = editor.getDocument(); + final SelectionModel selectionModel = editor.getSelectionModel(); + final CaretModel caretModel = editor.getCaretModel(); + + + if (hint != null) { + hint.addHintListener(hintListener); + } + document.addDocumentListener(documentListener); + selectionModel.addSelectionListener(selectionListener); + caretModel.addCaretListener(caretListener); + + Disposer.register(this, new Disposable() { + @Override + public void dispose() { + if (hint != null) { + hint.removeHintListener(hintListener); + hint.hide(); + } + document.removeDocumentListener(documentListener); + selectionModel.removeSelectionListener(selectionListener); + caretModel.removeCaretListener(caretListener); + } + }); + } + + } + + public static class InsertedSingleItem extends ZombiePhase { + public InsertedSingleItem(CompletionProgressIndicator indicator) { + super(null, indicator); + } + } + public static class NoSuggestionsHint extends ZombiePhase { + public NoSuggestionsHint(@Nullable LightweightHint hint, CompletionProgressIndicator indicator) { + super(hint, indicator); + } + } + public static class PossiblyDisturbingAutoPopup extends CompletionPhase {} + public static class EmptyAutoPopup extends CompletionPhase {} + +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index e88451ece076..3b3dbc4690dd 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -25,11 +25,7 @@ import com.intellij.openapi.application.Result; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; @@ -47,8 +43,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.ReferenceRange; import com.intellij.psi.util.PsiUtilBase; -import com.intellij.ui.HintListener; -import com.intellij.ui.LightweightHint; import com.intellij.util.ObjectUtils; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; @@ -62,7 +56,6 @@ import javax.swing.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.Collections; -import java.util.EventObject; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -146,6 +139,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } void notifyBackgrounded() { + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.BgCalculation()); myState.setBackgrounded(); } @@ -197,6 +191,10 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } } + public CompletionState getCompletionState() { + return myState; + } + private static int findReplacementOffset(int selectionEndOffset, PsiReference reference) { final List ranges = ReferenceRange.getAbsoluteRanges(reference); for (TextRange range : ranges) { @@ -287,20 +285,20 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement private void processModifier(KeyEvent e) { final int code = e.getKeyCode(); if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_META || code == KeyEvent.VK_ALT || code == KeyEvent.VK_SHIFT) { + contentComponent.removeKeyListener(this); myState.modifiersChanged(); if (myState.isWaitingAfterAutoInsertion()) { - unregisterItself(true); + myState.handleDeath(); + CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + assert currentCompletion == null : currentCompletion; + + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } - contentComponent.removeKeyListener(this); } } }); } - boolean isZombie() { - return myState.isZombie(); - } - private void setMergeCommand() { CommandProcessor.getInstance().setCurrentCommandGroupId(getCompletionCommandName()); } @@ -321,63 +319,12 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement CompletionServiceImpl.getCompletionService().setCurrentCompletion(this); } - public void liveAfterDeath(@Nullable final LightweightHint hint) { + void assertDisposed() { myState.assertDisposed(); + } - if (myState.areModifiersChanged() || ApplicationManager.getApplication().isUnitTestMode()) { - return; - } - - - registerItself(); - - - final HintListener hintListener = new HintListener() { - public void hintHidden(final EventObject event) { - unregisterItself(true); - } - }; - final DocumentAdapter documentListener = new DocumentAdapter() { - @Override - public void beforeDocumentChange(DocumentEvent e) { - unregisterItself(true); - } - }; - final SelectionListener selectionListener = new SelectionListener() { - public void selectionChanged(SelectionEvent e) { - unregisterItself(true); - } - }; - final CaretListener caretListener = new CaretListener() { - public void caretPositionChanged(CaretEvent e) { - unregisterItself(true); - } - }; - - final Document document = myEditor.getDocument(); - final SelectionModel selectionModel = myEditor.getSelectionModel(); - final CaretModel caretModel = myEditor.getCaretModel(); - - - if (hint != null) { - hint.addHintListener(hintListener); - } - document.addDocumentListener(documentListener); - selectionModel.addSelectionListener(selectionListener); - caretModel.addCaretListener(caretListener); - - myState.goZombie(hint, new Runnable() { - @Override - public void run() { - if (hint != null) { - hint.removeHintListener(hintListener); - } - document.removeDocumentListener(documentListener); - selectionModel.removeSelectionListener(selectionListener); - caretModel.removeCaretListener(caretListener); - } - }); - + boolean areModifiersChanged() { + return myState.areModifiersChanged(); } public CodeCompletionHandlerBase getHandler() { @@ -453,21 +400,15 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement public void closeAndFinish(boolean hideLookup) { LOG.assertTrue(this == CompletionServiceImpl.getCompletionService().getCurrentCompletion()); - if (myState.getCompletionHint() != null) { - myState.getCompletionHint().hide(); - } - Lookup lookup = LookupManager.getActiveLookup(myEditor); - if (lookup != null) { - LOG.assertTrue(lookup == myLookup); - myLookup.removeLookupListener(myLookupListener); - finishCompletionProcess(); + LOG.assertTrue(lookup == myLookup); + myLookup.removeLookupListener(myLookupListener); + finishCompletionProcess(); + myState.assertDisposed(); + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); - if (hideLookup) { - LookupManager.getInstance(getProject()).hideActiveLookup(); - } - } else { - myState.assertDisposed(); + if (hideLookup) { + LookupManager.getInstance(getProject()).hideActiveLookup(); } } @@ -479,7 +420,15 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement ApplicationManager.getApplication().assertIsDispatchThread(); Disposer.dispose(myQueue); - unregisterItself(false); + + myState.handleDeath(); + + CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + assert currentCompletion == this : currentCompletion + "!=" + this; + CompletionServiceImpl.getCompletionService().setCurrentCompletion(null); + + CompletionServiceImpl.assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class, CompletionPhase.Restarted.class); + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } @TestOnly @@ -490,13 +439,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } } - private void unregisterItself(boolean afterDeath) { - myState.handleDeath(afterDeath); - CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); - assert currentCompletion == this : currentCompletion + "!=" + this; - CompletionServiceImpl.getCompletionService().setCurrentCompletion(null); - } - public void stop() { super.stop(); @@ -507,22 +449,32 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement public void run() { if (isOutdated()) return; if (!isBackgrounded()) return; - if (isCanceled() && !myState.isRestartScheduled()) return; + + if (isCanceled() && myState.isRestartScheduled()) { + CompletionServiceImpl.assertPhase(CompletionPhase.Restarted.class); + return; + } myLookup.setCalculating(false); - if (hideAutopopupIfMeaningless()) { + if (isCanceled()) { + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); + return; + } + + if (CompletionServiceImpl.isPhase(CompletionPhase.BgCalculation.class) && hideAutopopupIfMeaningless()) { return; } if (myState.hasNoVariants()) { - LookupManager.getInstance(getProject()).hideActiveLookup(); - - final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); - LOG.assertTrue(current == null, current + "!=" + CompletionProgressIndicator.this); - if (!isAutopopupCompletion()) { - myHandler.handleEmptyLookup(getProject(), myEditor, myParameters, CompletionProgressIndicator.this); + LookupManager.getInstance(getProject()).hideActiveLookup(); + + final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + LOG.assertTrue(current == null, current + "!=" + CompletionProgressIndicator.this); + + CompletionServiceImpl + .setCompletionPhase(myHandler.handleEmptyLookup(getProject(), myEditor, myParameters, CompletionProgressIndicator.this)); } } else { @@ -530,6 +482,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement myLookup.setFocused(true); } updateLookup(); + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated()); } } }, myQueue.getModalityState()); @@ -542,6 +495,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement if (items.isEmpty() && !myLookup.isCalculating()) { myLookup.hideLookup(false); LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null); + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.EmptyAutoPopup()); return true; } @@ -549,6 +503,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement if ((item.getPrefixMatcher().getPrefix() + myLookup.getAdditionalPrefix()).equals(item.getLookupString())) { myLookup.hideLookup(true); // so that the autopopup attempts to restart after the next typed character LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null); + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.PossiblyDisturbingAutoPopup()); return true; } } @@ -585,8 +540,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void restorePrefix() { - closeAndFinish(false); - new WriteCommandAction(getProject(), getCompletionCommandName()) { @Override protected void run(Result result) throws Throwable { @@ -676,6 +629,8 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement public void scheduleRestart() { ApplicationManager.getApplication().assertIsDispatchThread(); myState.scheduleRestart(); + final CompletionPhase phase = new CompletionPhase.Restarted(); + CompletionServiceImpl.setCompletionPhase(phase); final Project project = getProject(); ApplicationManager.getApplication().invokeLater(new Runnable() { @@ -684,6 +639,9 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement if (isOutdated()) { return; } + if (phase != CompletionServiceImpl.getCompletionPhase()) { + return; + } closeAndFinish(false); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java index 979b3c76f21c..54c1e10dc93f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java @@ -1,10 +1,8 @@ package com.intellij.codeInsight.completion; +import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.ui.LightweightHint; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author peter @@ -13,7 +11,6 @@ public class CompletionState { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionState"); private boolean myCompletionDisposed; private boolean myShownLookup; - private LightweightHint myCompletionHint; private Boolean myToRestart; private boolean myRestartScheduled; private boolean myModifiersChanged; @@ -21,7 +18,6 @@ public class CompletionState { private boolean myBackgrounded; private volatile boolean myFocusLookupWhenDone; private volatile int myCount; - private Runnable myZombieCleanup; public CompletionState(boolean shownLookup) { myShownLookup = shownLookup; @@ -34,7 +30,6 @@ public class CompletionState { public void setCompletionDisposed(boolean completionDisposed) { LOG.assertTrue(!myCompletionDisposed, this); LOG.assertTrue(!isWaitingAfterAutoInsertion(), this); - LOG.assertTrue(myCompletionHint == null, this); myCompletionDisposed = completionDisposed; } @@ -46,21 +41,6 @@ public class CompletionState { myShownLookup = shownLookup; } - public LightweightHint getCompletionHint() { - return myCompletionHint; - } - - public void goZombie(@Nullable LightweightHint completionHint, @NotNull Runnable cleanup) { - LOG.assertTrue(myZombieCleanup == null, this); - if (completionHint != null) { - LOG.assertTrue(myCompletionHint == null, this); - } else { - LOG.assertTrue(!myModifiersChanged, this); - } - myCompletionHint = completionHint; - myZombieCleanup = cleanup; - } - public boolean isToRestart() { return myToRestart == Boolean.TRUE; } @@ -91,10 +71,16 @@ public class CompletionState { } public boolean isWaitingAfterAutoInsertion() { - return myRestorePrefix != null && isZombie(); + CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); + if (phase instanceof CompletionPhase.InsertedSingleItem && ((CompletionPhase.InsertedSingleItem)phase).indicator.getCompletionState() == this) { + LOG.assertTrue(myRestorePrefix != null, this); + return true; + } + return false; } public void setRestorePrefix(Runnable restorePrefix) { + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); myRestorePrefix = restorePrefix; } @@ -121,27 +107,17 @@ public class CompletionState { } public void restorePrefix() { + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); if (myRestorePrefix != null) { myRestorePrefix.run(); myRestorePrefix = null; } } - public void handleDeath(boolean afterDeath) { + public void handleDeath() { ApplicationManager.getApplication().assertIsDispatchThread(); - boolean zombie = isZombie(); - LOG.assertTrue(afterDeath == zombie, this); assertDisposed(); - if (zombie) { - myZombieCleanup.run(); - } - myZombieCleanup = null; - myCompletionHint = null; - setRestorePrefix(null); - } - - public boolean isZombie() { - return myZombieCleanup != null; + myRestorePrefix = null; } int incCount() { @@ -155,9 +131,9 @@ public class CompletionState { @Override public String toString() { return "CompletionState{" + - "myCompletionDisposed=" + myCompletionDisposed + + "phase=" + CompletionServiceImpl.getCompletionPhase() + + ", myCompletionDisposed=" + myCompletionDisposed + ", myShownLookup=" + myShownLookup + - ", myCompletionHint=" + myCompletionHint + ", myToRestart=" + myToRestart + ", myRestartScheduled=" + myRestartScheduled + ", myModifiersReleased=" + myModifiersChanged + @@ -165,7 +141,6 @@ public class CompletionState { ", myBackgrounded=" + myBackgrounded + ", myFocusLookupWhenDone=" + myFocusLookupWhenDone + ", myCount=" + myCount + - ", myZombieCleanup=" + myZombieCleanup + '}'; } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java index fd8ecf13cd62..8d263cc0b83a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java @@ -20,10 +20,12 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.DebugUtil; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,6 +37,8 @@ public class CompletionServiceImpl extends CompletionService{ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.impl.CompletionServiceImpl"); private Throwable myTrace = null; private CompletionProgressIndicator myCurrentCompletion; + private static CompletionPhase ourPhase = CompletionPhase.NoCompletion; + private static String ourPhaseTrace; public static CompletionServiceImpl getCompletionService() { return (CompletionServiceImpl)CompletionService.getCompletionService(); @@ -138,7 +142,13 @@ public class CompletionServiceImpl extends CompletionService{ final String newLookupString = handleCaseInsensitiveVariant(prefix, oldLookupString); if (!newLookupString.equals(oldLookupString)) { final Document document = context.getEditor().getDocument(); - document.replaceString(context.getStartOffset(), context.getTailOffset(), newLookupString); + int startOffset = context.getStartOffset(); + int tailOffset = context.getTailOffset(); + + assert startOffset >= 0 : "stale startOffset"; + assert tailOffset >= 0 : "stale tailOffset"; + + document.replaceString(startOffset, tailOffset, newLookupString); PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); } } @@ -162,4 +172,31 @@ public class CompletionServiceImpl extends CompletionService{ return lookupString; } + public static void assertPhase(Class... possibilities) { + if (!isPhase(possibilities)) { + LOG.error(ourPhase + "; set at " + ourPhaseTrace); + } + } + + public static boolean isPhase(Class... possibilities) { + ApplicationManager.getApplication().assertIsDispatchThread(); + for (Class possibility : possibilities) { + if (possibility.isInstance(ourPhase)) { + return true; + } + } + return false; + } + + public static void setCompletionPhase(@NotNull CompletionPhase phase) { + ApplicationManager.getApplication().assertIsDispatchThread(); + Disposer.dispose(ourPhase); + ourPhase = phase; + ourPhaseTrace = DebugUtil.currentStackTrace(); + } + + public static CompletionPhase getCompletionPhase() { +// ApplicationManager.getApplication().assertIsDispatchThread(); + return ourPhase; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java index 7504d8b52dc6..0e8591c8a1da 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.AutoPopupController; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; +import com.intellij.codeInsight.completion.CompletionPhase; import com.intellij.codeInsight.completion.CompletionProgressIndicator; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; @@ -93,9 +94,14 @@ public class CompletionAutoPopupHandler extends TypedHandlerDelegate { final boolean isMainEditor = FileEditorManager.getInstance(project).getSelectedTextEditor() == editor; + final CompletionPhase.AutoPopupAlarm phase = new CompletionPhase.AutoPopupAlarm(); + CompletionServiceImpl.setCompletionPhase(phase); + final Runnable request = new Runnable() { @Override public void run() { + if (CompletionServiceImpl.getCompletionPhase() != phase) return; + if (project.isDisposed() || !file.isValid()) return; if (editor.isDisposed() || isMainEditor && FileEditorManager.getInstance(project).getSelectedTextEditor() != editor) return; if (ApplicationManager.getApplication().isWriteAccessAllowed()) return; //it will fail anyway @@ -152,9 +158,16 @@ public class CompletionAutoPopupHandler extends TypedHandlerDelegate { } final CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); - if (currentCompletion != null) { - currentCompletion.closeAndFinish(true); + if (CompletionServiceImpl.isPhase(CompletionPhase.AutoPopupAlarm.class, CompletionPhase.EmptyAutoPopup.class, CompletionPhase.PossiblyDisturbingAutoPopup.class)) { + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + assert currentCompletion == null; + } else { + if (currentCompletion != null) { + currentCompletion.closeAndFinish(true); + } + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); } + state.stopAutoPopup(); } diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index bf88b90692d8..57f3011f0f12 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -54,6 +54,7 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; @@ -839,35 +840,42 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig public LookupElement[] complete(final CompletionType type, final int invocationCount) { assertInitialized(); myEmptyLookup = false; - new WriteCommandAction(getProject()) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override - protected void run(Result result) throws Exception { - final CodeCompletionHandlerBase handler = new CodeCompletionHandlerBase(type) { + public void run() { + CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() { @Override - protected PsiFile createFileCopy(final PsiFile file) { - final PsiFile copy = super.createFileCopy(file); - if (myFileContext != null) { - final PsiElement contextCopy = myFileContext.copy(); - final PsiFile containingFile = contextCopy.getContainingFile(); - if (containingFile instanceof PsiFileImpl) { - ((PsiFileImpl)containingFile).setOriginalFile(myFileContext.getContainingFile()); + public void run() { + final CodeCompletionHandlerBase handler = new CodeCompletionHandlerBase(type) { + @Override + protected PsiFile createFileCopy(final PsiFile file) { + final PsiFile copy = super.createFileCopy(file); + if (myFileContext != null) { + final PsiElement contextCopy = myFileContext.copy(); + final PsiFile containingFile = contextCopy.getContainingFile(); + if (containingFile instanceof PsiFileImpl) { + ((PsiFileImpl)containingFile).setOriginalFile(myFileContext.getContainingFile()); + } + setContext(copy, contextCopy); + } + return copy; } - setContext(copy, contextCopy); - } - return copy; - } - @Override - protected void completionFinished(final int offset1, final int offset2, final CompletionProgressIndicator indicator, - final LookupElement[] items) { - myEmptyLookup = items.length == 0; - super.completionFinished(offset1, offset2, indicator, items); + @Override + protected void completionFinished(final int offset1, final int offset2, final CompletionProgressIndicator indicator, + final LookupElement[] items) { + myEmptyLookup = items.length == 0; + super.completionFinished(offset1, offset2, indicator, items); + } + }; + Editor editor = getCompletionEditor(); + handler.invokeCompletion(getProject(), editor, PsiUtilBase.getPsiFileInEditor(editor, getProject()), invocationCount); + } - }; - Editor editor = getCompletionEditor(); - handler.invokeCompletion(getProject(), editor, PsiUtilBase.getPsiFileInEditor(editor, getProject()), invocationCount); + }, null, null); } - }.execute(); + }); + return getLookupElements(); } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index c732c03a543d..0fe6db6ebcdd 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -781,7 +781,7 @@ order="before openedInEditor"/> -