diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 3769a436238e..05c6c04977b6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -120,7 +120,18 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE); - final CompletionProgressIndicator indicator = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + CompletionProgressIndicator indicator = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); + if (indicator != null) { + indicator.closeAndFinish(false); + } else { + CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); + if (phase instanceof CompletionPhase.ZombiePhase) { + indicator = ((CompletionPhase.ZombiePhase)phase).indicator; + } + } + + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + if (indicator != null) { boolean repeated = indicator.isRepeatedInvocation(myCompletionType, editor); if (repeated && !indicator.isRunning() && (!isAutocompleteCommonPrefixOnInvocation() || indicator.fillInCommonPrefix(true))) { @@ -130,8 +141,6 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { if (repeated) { time = Math.max(indicator.getParameters().getInvocationCount() + 1, 2); indicator.restorePrefix(); - } else { - indicator.closeAndFinish(false); } } @@ -492,11 +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); - CompletionServiceImpl.setCompletionPhase(new CompletionPhase.InsertedSingleItem()); - } else { - LOG.assertTrue(!indicator.isZombie(), indicator); + if (CompletionService.getCompletionService().getCurrentCompletion() == null && + !ApplicationManager.getApplication().isUnitTestMode()) { + CompletionServiceImpl.setCompletionPhase(new CompletionPhase.InsertedSingleItem(indicator)); + assert indicator.getCompletionState().isWaitingAfterAutoInsertion(); } } } @@ -601,21 +609,26 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { if (!invokedExplicitly) { return CompletionPhase.NoCompletion; } + indicator.assertDisposed(); + assert !indicator.isAutopopupCompletion(); - CompletionPhase result = CompletionPhase.NoCompletion; + 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(); - result = new CompletionPhase.NoSuggestionsHint(); break; } } @@ -623,7 +636,7 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { if (codeAnalyzer != null) { codeAnalyzer.updateVisibleHighlighters(editor); } - return result; + return result[0]; } private static void lookupItemSelected(final CompletionProgressIndicator indicator, @NotNull final LookupElement item, final char completionChar, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java index 91c79cb9ed0a..3f2432e9bb51 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java @@ -15,19 +15,103 @@ */ 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 { +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 InsertedSingleItem extends CompletionPhase {} - public static class NoSuggestionsHint 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 c21e82e1bccc..bffcdccabf70 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; @@ -198,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) { @@ -288,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()); } @@ -322,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() { @@ -454,10 +400,6 @@ 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); @@ -480,8 +422,14 @@ 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); } @@ -493,19 +441,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } } - private void unregisterItself(boolean afterDeath) { - if (afterDeath) { - CompletionServiceImpl.assertPhase(CompletionPhase.NoSuggestionsHint.class, CompletionPhase.InsertedSingleItem.class); - } else { - CompletionServiceImpl.assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class, CompletionPhase.Restarted.class); - } - - myState.handleDeath(afterDeath); - CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); - assert currentCompletion == this : currentCompletion + "!=" + this; - CompletionServiceImpl.getCompletionService().setCurrentCompletion(null); - } - public void stop() { super.stop(); @@ -599,8 +534,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public void restorePrefix() { - closeAndFinish(false); - new WriteCommandAction(getProject(), getCompletionCommandName()) { @Override protected void run(Result result) throws Throwable { 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 ae1ed490e071..54c1e10dc93f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionState.java @@ -3,9 +3,6 @@ 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 @@ -14,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; @@ -22,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; @@ -35,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; } @@ -47,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; } @@ -92,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; } @@ -122,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() { @@ -159,7 +134,6 @@ public class CompletionState { "phase=" + CompletionServiceImpl.getCompletionPhase() + ", myCompletionDisposed=" + myCompletionDisposed + ", myShownLookup=" + myShownLookup + - ", myCompletionHint=" + myCompletionHint + ", myToRestart=" + myToRestart + ", myRestartScheduled=" + myRestartScheduled + ", myModifiersReleased=" + myModifiersChanged + @@ -167,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 87c9556fa3f1..87747a060f18 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,6 +20,7 @@ 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; @@ -187,6 +188,7 @@ public class CompletionServiceImpl extends CompletionService{ public static void setCompletionPhase(@NotNull CompletionPhase phase) { ApplicationManager.getApplication().assertIsDispatchThread(); + Disposer.dispose(ourPhase); ourPhase = phase; ourPhaseTrace = DebugUtil.currentStackTrace(); }