zombie phases manage themselves

This commit is contained in:
peter
2011-01-25 19:28:57 +01:00
parent 1737b0668c
commit 81466e150e
5 changed files with 145 additions and 140 deletions
@@ -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,
@@ -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 {}
@@ -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<TextRange> 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 {
@@ -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 +
'}';
}
}
@@ -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();
}