diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index bbff39c7d03e..7f5bb5abca6f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -246,8 +246,9 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi if (task.indicator.isCanceled()) { s += "; indicator: " + task.indicator; } + Document document = task.getDocument(); boolean stillUncommitted = !task.project.isDisposed() && - ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(task.project)).isInUncommittedSet(task.document); + ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(task.project)).isInUncommittedSet(document); if (stillUncommitted) { s += "; still uncommitted"; } @@ -345,11 +346,11 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } } - private static boolean cancelAndRemoveFromQueue(@NotNull CommitTask newTask, @NotNull HashSetQueue queue, @NotNull Object reason) { + private boolean cancelAndRemoveFromQueue(@NotNull CommitTask newTask, @NotNull HashSetQueue queue, @NotNull Object reason) { CommitTask queuedTask = queue.find(newTask); if (queuedTask != null) { assert queuedTask != newTask; - queuedTask.cancel(reason, getInstance()); + queuedTask.cancel(reason, this); } return queue.remove(newTask); } @@ -382,7 +383,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi return false; } - document = task.document; + document = task.getDocument(); indicator = task.indicator; project = task.project; @@ -413,7 +414,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi public void run() { result.set(commitUnderProgress(commitTask, false)); } - }, commitTask.indicator); + }, indicator); finishRunnable = result.get().first; success = finishRunnable != null; failureReason = result.get().second; @@ -435,7 +436,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } } catch (ProcessCanceledException e) { - cancel(e + " (cancel reason: "+((UserDataHolder)task.indicator).getUserData(CommitTask.CANCEL_REASON)+")"); // leave queue unchanged + cancel(e + " (cancel reason: "+((UserDataHolder)task.indicator).getUserData(CANCEL_REASON)+")"); // leave queue unchanged success = false; failureReason = e; } @@ -446,7 +447,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); - if (!success && task != null && documentManager.isUncommited(task.document)) { // sync commit has not intervened + if (!success && task != null && documentManager.isUncommited(document)) { // sync commit has not intervened final Document finalDocument = document; final Project finalProject = project; List> oldFileNodes = @@ -541,9 +542,9 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi if (synchronously) { assert !task.indicator.isCanceled(); } - final Project project = task.project; - final Document document = task.document; + final Document document = task.getDocument(); + final Project project = task.project; final PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(project); final List> finishProcessors = new SmartList>(); Runnable runnable = new Runnable() { @@ -602,11 +603,21 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi return new Pair(null, "Indicator was canceled"); } - Runnable result = new Runnable() { + Runnable result = createEdtRunnable(task, synchronously, finishProcessors); + return Pair.create(result, null); + } + + @NotNull + private Runnable createEdtRunnable(@NotNull final CommitTask task, + final boolean synchronously, + @NotNull final List> finishProcessors) { + return new Runnable() { @Override public void run() { myApplication.assertIsDispatchThread(); - + Document document = task.getDocument(); + Project project = task.project; + PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(project); boolean committed = project.isDisposed() || documentManager.isCommitted(document); synchronized (lock) { documentsToApplyInEDT.remove(task); @@ -643,7 +654,6 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } } }; - return Pair.create(result, null); } @NotNull @@ -683,9 +693,9 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi UIUtil.dispatchAllInvocationEvents(); } - private static class CommitTask { - static final Key CANCEL_REASON = Key.create("CANCEL_REASON"); - @NotNull final Document document; + private static final Key CANCEL_REASON = Key.create("CANCEL_REASON"); + private class CommitTask { + @NotNull private final Document document; @NotNull final Project project; private final int modificationSequence; // store initial document modification sequence here to check if it changed later before commit in EDT @@ -698,12 +708,12 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi private final CharSequence myLastCommittedText; @NotNull final List> myOldFileNodes; - protected CommitTask(@NotNull final Project project, - @NotNull final Document document, - @NotNull final List> oldFileNodes, - @NotNull ProgressIndicator indicator, - @NotNull Object reason, - @NotNull ModalityState currentModalityState) { + CommitTask(@NotNull final Project project, + @NotNull final Document document, + @NotNull final List> oldFileNodes, + @NotNull ProgressIndicator indicator, + @NotNull Object reason, + @NotNull ModalityState currentModalityState) { this.document = document; this.project = project; this.indicator = indicator; @@ -717,11 +727,13 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi @NonNls @Override public String toString() { - return "Doc: " + document + " (\"" + StringUtil.first(document.getImmutableCharSequence(), 40, true).toString().replaceAll("\n", " ") + "\")" - + (indicator.isCanceled() ? " (Canceled: " + ((UserDataHolder)indicator).getUserData(CANCEL_REASON) + ")":"") - +" Reason: " + reason - + (isStillValid() ? "" : "; changed: old seq="+modificationSequence+", new seq="+ ((DocumentEx)document).getModificationSequence()) - ; + Document document = getDocument(); + String docInfo = document + " (\"" + StringUtil.first(document.getImmutableCharSequence(), 40, true).toString().replaceAll("\n", " ") + "\")"; + String indicatorInfo = indicator.isCanceled() ? " (Canceled: " + ((UserDataHolder)indicator).getUserData(CANCEL_REASON) + ")" : ""; + String reasonInfo = " Reason: " + reason + (isStillValid() ? "" + : "; changed: old seq=" + modificationSequence + ", new seq=" + + ((DocumentEx)document).getModificationSequence()); + return "Doc: " + docInfo + indicatorInfo + reasonInfo; } @Override @@ -731,17 +743,18 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi CommitTask task = (CommitTask)o; - return document.equals(task.document) && project.equals(task.project); + return Comparing.equal(getDocument(),task.getDocument()) && project.equals(task.project); } @Override public int hashCode() { - int result = document.hashCode(); + int result = getDocument().hashCode(); result = 31 * result + project.hashCode(); return result; } public boolean isStillValid() { + Document document = getDocument(); return ((DocumentEx)document).getModificationSequence() == modificationSequence; } @@ -751,8 +764,17 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi indicator.cancel(); ((UserDataHolder)indicator).putUserData(CANCEL_REASON, reason); + + synchronized (lock) { + documentsToCommit.remove(this); + documentsToApplyInEDT.remove(this); + } } } + + Document getDocument() { + return document; + } } // public for Upsource @@ -760,7 +782,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi public Processor doCommit(@NotNull final CommitTask task, @NotNull final PsiFile file, @NotNull final FileASTNode oldFileNode) { - Document document = task.document; + Document document = task.getDocument(); final CharSequence newDocumentText = document.getImmutableCharSequence(); final TextRange changedPsiRange = getChangedPsiRange(file, task.myLastCommittedText, newDocumentText); if (changedPsiRange == null) { @@ -944,4 +966,13 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi return lock != null ? lock : ((UserDataHolderEx)document).putUserDataIfAbsent(DOCUMENT_LOCK, new ReentrantLock()); } private static final Key DOCUMENT_LOCK = Key.create("DOCUMENT_LOCK"); + + @TestOnly + int documentsToCommit() { + return documentsToCommit.size(); + } + @TestOnly + int documentsToApplyInEDT() { + return documentsToApplyInEDT.size(); + } }