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 4d62dd82a611..d2f0aaf07ce8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -39,6 +39,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.actionSystem.EditorActionManager; import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.editor.ex.RangeMarkerEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProcessCanceledException; @@ -541,10 +542,7 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { } 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, - completionChar != Lookup.AUTO_INSERT_SELECT_CHAR && completionChar != Lookup.REPLACE_SELECT_CHAR && - completionChar != Lookup.NORMAL_SELECT_CHAR && completionChar != Lookup.COMPLETE_STATEMENT_SELECT_CHAR); + final WatchingInsertionContext context = new WatchingInsertionContext(indicator, completionChar, items, editor); ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { final int idEndOffset = indicator.getIdentifierEndOffset(); @@ -571,6 +569,7 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { else { LOG.error("tailOffset<0 after inserting " + item + " of " + item.getClass()); } + context.stopWatching(); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } }); @@ -578,9 +577,9 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { if (runnable != null) { final Runnable runnable1 = new Runnable() { public void run() { - final Project project = context.getProject(); - if (project.isDisposed()) return; - runnable.run(); + if (!context.getProject().isDisposed()) { + runnable.run(); + } } }; if (!ApplicationManager.getApplication().isUnitTestMode()) { @@ -685,4 +684,44 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { } }; } + + private static class WatchingInsertionContext extends InsertionContext { + private RangeMarkerEx tailWatcher; + + public WatchingInsertionContext(CompletionProgressIndicator indicator, char completionChar, List items, Editor editor) { + super(indicator.getOffsetMap(), completionChar, items.toArray(new LookupElement[items.size()]), + indicator.getParameters().getOriginalFile(), editor, + completionChar != Lookup.AUTO_INSERT_SELECT_CHAR && completionChar != Lookup.REPLACE_SELECT_CHAR && + completionChar != Lookup.NORMAL_SELECT_CHAR && completionChar != Lookup.COMPLETE_STATEMENT_SELECT_CHAR); + } + + @Override + public void setTailOffset(int offset) { + super.setTailOffset(offset); + watchTail(offset); + } + + private void watchTail(int offset) { + stopWatching(); + tailWatcher = (RangeMarkerEx)getDocument().createRangeMarker(offset, offset); + tailWatcher.trackInvalidation(true); + tailWatcher.setGreedyToRight(true); + } + + void stopWatching() { + if (tailWatcher != null) { + tailWatcher.trackInvalidation(false); + } + } + + @Override + public int getTailOffset() { + int offset = super.getTailOffset(); + if (tailWatcher.getStartOffset() != tailWatcher.getEndOffset()) { + watchTail(offset); + } + + return offset; + } + } } diff --git a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java index 6697e3127cef..82e9098b5200 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java @@ -60,6 +60,11 @@ public class RangeMarkerWindow implements RangeMarkerEx { return myHostMarker.setValid(value); } + @Override + public void trackInvalidation(boolean track) { + myHostMarker.trackInvalidation(track); + } + ////////////////////////////delegates public void setGreedyToLeft(final boolean greedy) { myHostMarker.setGreedyToLeft(greedy); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java index 7ba51ea1148d..190d6c6c28bb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java @@ -33,4 +33,7 @@ public interface RangeMarkerEx extends RangeMarker, MutableInterval, Segment { void documentChanged(DocumentEvent e); long getId(); + + void trackInvalidation(boolean track); + } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java index 07e1e7df6b1c..2f52d3a489e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java @@ -30,6 +30,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx protected final DocumentEx myDocument; RangeMarkerTree.RMNode myNode; + private boolean myTrackInvalidation; private final long myId; //private static long counter; @@ -236,8 +237,17 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx return node != null && node.isValid(); } + @Override + public void trackInvalidation(boolean track) { + myTrackInvalidation = track; + } + @Override public boolean setValid(boolean value) { + if (!value && myTrackInvalidation) { + LOG.error("Range marker invalidated"); + } + RangeMarkerTree.RMNode node = myNode; return node == null || node.setValid(value); }