From 606b9a025e3e59ba19eebb11ce72db81bbf272bd Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 12 Aug 2011 13:17:07 +0200 Subject: [PATCH] a single phase for original & copy commit, no restart phase => no finishing character swallowing => PROFIT!!!! --- .../completion/JavaAutoPopupTest.groovy | 2 +- .../codeInsight/AutoPopupController.java | 28 +++++----- .../completion/CodeCompletionHandlerBase.java | 55 ++++++++++++------- .../completion/CompletionPhase.java | 55 ++++++++++++------- .../CompletionProgressIndicator.java | 30 ++++++---- .../impl/CompletionServiceImpl.java | 4 +- .../CompletionAutoPopupHandler.java | 33 +++++------ .../macro/ClassNameCompleteMacro.java | 2 +- .../template/macro/CompleteMacro.java | 2 +- 9 files changed, 119 insertions(+), 92 deletions(-) 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 0b31df1b68f9..9f1483f0f11a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -726,7 +726,7 @@ class Foo { assert !lookup } - public void _testChoosingItemDuringCopyCommit() { + public void testChoosingItemDuringCopyCommit() { registerContributor(LongReplacementOffsetContributor) myFixture.configureByText("a.java", """ class Foo { { int iteraaa; } } """) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java index 0dfd5aac36a2..c2baeeb502ca 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java @@ -22,9 +22,8 @@ import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; import com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler; import com.intellij.codeInsight.hint.ShowParameterInfoHandler; -import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.ide.IdeEventQueue; +import com.intellij.ide.PowerSaveMode; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -90,24 +89,27 @@ public class AutoPopupController implements Disposable { } public void scheduleAutoPopup(final Editor editor, @Nullable final Condition condition) { - if (ApplicationManager.getApplication().isUnitTestMode() && - !CompletionAutoPopupHandler.ourTestingAutopopup) { + if (ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup) { return; } if (!CodeInsightSettings.getInstance().AUTO_POPUP_COMPLETION_LOOKUP) { return; } - - LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); - boolean shouldBeFast = lookup != null && lookup.isShown(); + if (PowerSaveMode.isEnabled()) { + return; + } + + if (!CompletionServiceImpl.isPhase(CompletionPhase.CommittingDocuments.class, CompletionPhase.NoCompletion.getClass())) { + return; + } final CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); if (currentCompletion != null) { - currentCompletion.closeAndFinish(!shouldBeFast); + currentCompletion.closeAndFinish(true); } - final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(false, editor); + final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(null, editor); CompletionServiceImpl.setCompletionPhase(phase); Runnable request = new Runnable() { @@ -116,7 +118,7 @@ public class AutoPopupController implements Disposable { CompletionAutoPopupHandler.runLaterWithCommitted(myProject, editor.getDocument(), new Runnable() { @Override public void run() { - if (phase.isExpired()) return; + if (phase.checkExpired()) return; PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument()); if (file != null && condition != null && !condition.value(file)) return; @@ -126,11 +128,7 @@ public class AutoPopupController implements Disposable { }); } }; - if (shouldBeFast) { - request.run(); - } else { - addRequest(request, CodeInsightSettings.getInstance().AUTO_LOOKUP_DELAY); - } + addRequest(request, CodeInsightSettings.getInstance().AUTO_LOOKUP_DELAY); } @TestOnly 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 39e2667638bb..9176f4edcc9d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight.completion; -import com.intellij.codeInsight.AutoPopupController; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; @@ -30,7 +29,6 @@ import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.ApplicationAdapter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.application.ex.ApplicationManagerEx; @@ -51,13 +49,15 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.CommitToPsiFileAction; import com.intellij.psi.impl.PsiFileEx; import com.intellij.psi.impl.PsiModificationTrackerImpl; import com.intellij.psi.impl.source.PostprocessReformattingAspect; @@ -79,16 +79,22 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CodeCompletionHandlerBase"); private final CompletionType myCompletionType; final boolean invokedExplicitly; + final boolean synchronous; final boolean autopopup; public CodeCompletionHandlerBase(final CompletionType completionType) { - this(completionType, true, false); + this(completionType, true, false, true); } - public CodeCompletionHandlerBase(CompletionType completionType, boolean invokedExplicitly, boolean autopopup) { + public CodeCompletionHandlerBase(CompletionType completionType, boolean invokedExplicitly, boolean autopopup, boolean synchronous) { myCompletionType = completionType; this.invokedExplicitly = invokedExplicitly; this.autopopup = autopopup; + this.synchronous = synchronous; + + if (invokedExplicitly) { + assert synchronous; + } } public final void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile psiFile) { @@ -132,8 +138,10 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { return; } - time = phase.newCompletionStarted(time, repeated); - CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); + if (invokedExplicitly) { + time = phase.newCompletionStarted(time, repeated); + } + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass(), CompletionPhase.CommittingDocuments.class); if (time > 1) { if (myCompletionType == CompletionType.CLASS_NAME) { @@ -256,6 +264,16 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { CompletionContext context = createCompletionContext(hostFile, hostStartOffset, hostEditor, hostMap); CompletionParameters parameters = createCompletionParameters(invocationCount, initContext, context); + CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); + if (phase instanceof CompletionPhase.CommittingDocuments) { + if (phase.indicator != null) { + phase.indicator.closeAndFinish(false); + } + ((CompletionPhase.CommittingDocuments)phase).replaced = true; + } else { + CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); + } + final Editor editor = initContext.getEditor(); final Semaphore freezeSemaphore = new Semaphore(); freezeSemaphore.down(); @@ -503,23 +521,18 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { final Project project = hostFile.getProject(); - if (!invokedExplicitly) { - final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(true, hostEditor); - CompletionServiceImpl.setCompletionPhase(phase); - - ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() { - @Override - public void beforeWriteActionStart(Object action) { - if (!(action instanceof Class) || !CommitToPsiFileAction.class.isAssignableFrom((Class)action)) { - AutoPopupController.getInstance(project).scheduleAutoPopup(hostEditor, Condition.TRUE); - } - } - }, phase); + if (!synchronous) { + if (!CompletionServiceImpl.assertPhase(CompletionPhase.CommittingDocuments.class)) { + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + return; + } + + final CompletionPhase.CommittingDocuments phase = (CompletionPhase.CommittingDocuments)CompletionServiceImpl.getCompletionPhase(); CompletionAutoPopupHandler.runLaterWithCommitted(project, hostDocument, new Runnable() { @Override public void run() { - if (phase.isExpired()) return; + if (phase.checkExpired()) return; doComplete(initContext, hasModifiers, invocationCount, hostFile, hostStartOffset, hostEditor, hostMap); } }); 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 b059109a220f..5c01ec59c7d3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionPhase.java @@ -73,15 +73,14 @@ public abstract class CompletionPhase implements Disposable { } public static class CommittingDocuments extends CompletionPhase { - final boolean copyCommit; + boolean replaced; private boolean actionsHappened; private final Editor myEditor; private final Expirable focusStamp; private final Project myProject; - public CommittingDocuments(boolean copyCommit, Editor editor) { - super(null); - this.copyCommit = copyCommit; + public CommittingDocuments(@Nullable CompletionProgressIndicator prevIndicator, Editor editor) { + super(prevIndicator); myEditor = editor; myProject = editor.getProject(); focusStamp = IdeFocusManager.getInstance(myProject).getTimestamp(false); @@ -93,21 +92,46 @@ public abstract class CompletionPhase implements Disposable { }, this); } - public boolean isExpired() { - if (ApplicationManager.getApplication().isWriteAccessAllowed()) return true; //it will fail anyway - if (actionsHappened) return true; - return CompletionServiceImpl.getCompletionPhase() != this || focusStamp.isExpired() || DumbService.getInstance(myProject).isDumb() || myEditor.isDisposed(); + public boolean checkExpired() { + if (CompletionServiceImpl.getCompletionPhase() != this) { + return true; + } + + if (actionsHappened || focusStamp.isExpired() || DumbService.getInstance(myProject).isDumb() || + myEditor.isDisposed() || + ApplicationManager.getApplication().isWriteAccessAllowed()) { + CompletionServiceImpl.setCompletionPhase(NoCompletion); + return true; + } + + return false; + } + + public boolean restartCompletion() { + if (indicator != null) { + replaced = true; + indicator.scheduleRestart(); + assert this != CompletionServiceImpl.getCompletionPhase(); + CompletionServiceImpl.assertPhase(CommittingDocuments.class); + } + return replaced; } @Override public int newCompletionStarted(int time, boolean repeated) { - CompletionServiceImpl.setCompletionPhase(NoCompletion); return time; } + @Override + public void dispose() { + if (!replaced && indicator != null) { + indicator.closeAndFinish(true); + } + } + @Override public String toString() { - return "CommittingDocuments{copyCommit=" + copyCommit + '}'; + return "CommittingDocuments{hasIndicator=" + (indicator != null) + '}'; } } public static class Synchronous extends CompletionPhase { @@ -164,17 +188,6 @@ public abstract class CompletionPhase implements Disposable { return indicator.fillInCommonPrefix(true); } } - public static class Restarted extends CompletionPhase { - public Restarted(CompletionProgressIndicator indicator) { - super(indicator); - } - - @Override - public int newCompletionStarted(int time, boolean repeated) { - indicator.closeAndFinish(false); - return indicator.nextInvocationCount(time, repeated); - } - } public static abstract class ZombiePhase 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 b87ac12fb412..3a4abeed7f92 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -429,7 +429,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement LOG.assertTrue(currentCompletion == this, currentCompletion + "!=" + this); CompletionServiceImpl.getCompletionService().setCurrentCompletion(null); - CompletionServiceImpl.assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class, CompletionPhase.Restarted.class); + CompletionServiceImpl.assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class, CompletionPhase.CommittingDocuments.class); + if (CompletionServiceImpl.getCompletionPhase() instanceof CompletionPhase.CommittingDocuments) { + LOG.assertTrue(CompletionServiceImpl.getCompletionPhase().indicator != null, CompletionServiceImpl.getCompletionPhase()); + ((CompletionPhase.CommittingDocuments)CompletionServiceImpl.getCompletionPhase()).replaced = true; + } CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); if (disposeOffsetMap) { disposeOffsetMaps(); @@ -628,23 +632,25 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); LOG.assertTrue(this == current, current + "!=" + this); - final CompletionPhase phase = new CompletionPhase.Restarted(this); + final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor); CompletionServiceImpl.setCompletionPhase(phase); final Project project = getProject(); - CompletionAutoPopupHandler.runLaterWithCommitted(project, myEditor.getDocument(), new Runnable() { + ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - if (phase != CompletionServiceImpl.getCompletionPhase()) { - return; - } - - closeAndFinish(false); - - CompletionAutoPopupHandler.invokeCompletion(myParameters.getCompletionType(), - isAutopopupCompletion(), project, myEditor, myParameters.getInvocationCount()); + CompletionAutoPopupHandler.runLaterWithCommitted(project, myEditor.getDocument(), new Runnable() { + @Override + public void run() { + if (phase.checkExpired()) return; + + CompletionAutoPopupHandler.invokeCompletion(myParameters.getCompletionType(), + isAutopopupCompletion(), project, myEditor, myParameters.getInvocationCount()); + } + }); } - }); + }, project.getDisposed()); + } @Override 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 c840766f3190..01877851cff7 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 @@ -197,10 +197,12 @@ public class CompletionServiceImpl extends CompletionService{ } } - public static void assertPhase(Class... possibilities) { + public static boolean assertPhase(Class... possibilities) { if (!isPhase(possibilities)) { LOG.error(ourPhase + "; set at " + ourPhaseTrace); + return false; } + return true; } public static boolean isPhase(Class... possibilities) { 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 a18c3ce34bfa..8f2149d10191 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CompletionAutoPopupHandler.java @@ -16,13 +16,11 @@ 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.CompletionType; import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.ide.PowerSaveMode; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -60,31 +58,28 @@ public class CompletionAutoPopupHandler extends TypedHandlerDelegate { @Override public Result checkAutoPopup(char charTyped, final Project project, final Editor editor, final PsiFile file) { - if (!CodeInsightSettings.getInstance().AUTO_POPUP_COMPLETION_LOOKUP) return Result.CONTINUE; - if (PowerSaveMode.isEnabled()) return Result.CONTINUE; - - if (LookupManager.getActiveLookup(editor) != null) { - return Result.CONTINUE; - } - CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase(); if (oldPhase instanceof CompletionPhase.EmptyAutoPopup && ((CompletionPhase.EmptyAutoPopup)oldPhase).editor != editor) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } - if (!Character.isLetter(charTyped) && charTyped != '_') { - if (CompletionServiceImpl.isPhase(CompletionPhase.EmptyAutoPopup.class, CompletionPhase.CommittingDocuments.class)) { - CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); - } - return Result.CONTINUE; + if (oldPhase instanceof CompletionPhase.CommittingDocuments && ((CompletionPhase.CommittingDocuments)oldPhase).restartCompletion()) { + return Result.STOP; } - if (!CompletionServiceImpl.isPhase(CompletionPhase.CommittingDocuments.class, CompletionPhase.NoCompletion.getClass())) { - return Result.CONTINUE; + if (LookupManager.getInstance(project).getActiveLookup() != null) { + return Result.STOP; } - AutoPopupController.getInstance(project).scheduleAutoPopup(editor, null); - return Result.STOP; + if (Character.isLetter(charTyped) || charTyped == '_') { + AutoPopupController.getInstance(project).scheduleAutoPopup(editor, null); + return Result.STOP; + } + + if (CompletionServiceImpl.isPhase(CompletionPhase.EmptyAutoPopup.class, CompletionPhase.CommittingDocuments.class)) { + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + } + return Result.CONTINUE; } public static void invokeCompletion(CompletionType completionType, @@ -104,7 +99,7 @@ public class CompletionAutoPopupHandler extends TypedHandlerDelegate { } Editor newEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(topLevelEditor, topLevelFile); try { - new CodeCompletionHandlerBase(completionType, false, autopopup).invokeCompletion(project, newEditor, time, false); + new CodeCompletionHandlerBase(completionType, false, autopopup, false).invokeCompletion(project, newEditor, time, false); } catch (IndexNotReadyException ignored) { } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/ClassNameCompleteMacro.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/ClassNameCompleteMacro.java index 6bf991033955..3f5e85574758 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/ClassNameCompleteMacro.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/ClassNameCompleteMacro.java @@ -25,6 +25,6 @@ public class ClassNameCompleteMacro extends BaseCompleteMacro { } CodeInsightActionHandler getCompletionHandler() { - return new CodeCompletionHandlerBase(CompletionType.CLASS_NAME, false, false); + return new CodeCompletionHandlerBase(CompletionType.CLASS_NAME, false, false, true); } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CompleteMacro.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CompleteMacro.java index 50dba61e1dba..6dfed72e5fb3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CompleteMacro.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/CompleteMacro.java @@ -26,6 +26,6 @@ public class CompleteMacro extends BaseCompleteMacro { } CodeInsightActionHandler getCompletionHandler() { - return new CodeCompletionHandlerBase(CompletionType.BASIC, ApplicationManager.getApplication().isUnitTestMode(), false); + return new CodeCompletionHandlerBase(CompletionType.BASIC, ApplicationManager.getApplication().isUnitTestMode(), false, true); } } \ No newline at end of file