private static void assertBeforeCommit( Document document, PsiFile file, TextBlock textBlock, CharSequence chars, String oldPsiText, FileElement myTreeElementBeingReparsedSoItWontBeCollected) { int startOffset = textBlock.getStartOffset(); int psiEndOffset = textBlock.getPsiEndOffset(); if (oldPsiText != null) { @NonNls String msg = "PSI/document inconsistency before reparse: "; if (startOffset >= oldPsiText.length()) { msg += "startOffset=" + oldPsiText + " while text length is " + oldPsiText.length() + "; "; startOffset = oldPsiText.length(); } String psiPrefix = oldPsiText.substring(0, startOffset); String docPrefix = chars.subSequence(0, startOffset).toString(); String psiSuffix = oldPsiText.substring(psiEndOffset); String docSuffix = chars.subSequence(textBlock.getTextEndOffset(), chars.length()).toString(); if (!psiPrefix.equals(docPrefix) || !psiSuffix.equals(docSuffix)) { if (!psiPrefix.equals(docPrefix)) { msg = msg + "psiPrefix=" + psiPrefix + "; docPrefix=" + docPrefix + ";"; } if (!psiSuffix.equals(docSuffix)) { msg = msg + "psiSuffix=" + psiSuffix + "; docSuffix=" + docSuffix + ";"; } throw new AssertionError(msg); } } else if (document.getTextLength() - textBlock.getTextEndOffset() != myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() - psiEndOffset) { throw new AssertionError("PSI/document inconsistency before reparse: file=" + file); } }
@Nullable("returns runnable to execute under write action in AWT to finish the commit") private Processor<Document> doCommit( @NotNull final Document document, @NotNull final PsiFile file, @NotNull ProgressIndicator indicator, final boolean synchronously, @NotNull PsiDocumentManager documentManager) { ((PsiDocumentManagerImpl) documentManager).clearTreeHardRef(document); final TextBlock textBlock = TextBlock.get(file); if (textBlock.isEmpty()) return null; final long startPsiModificationTimeStamp = file.getModificationStamp(); final long startDocModificationTimeStamp = document.getModificationStamp(); final FileElement myTreeElementBeingReparsedSoItWontBeCollected = ((PsiFileImpl) file).calcTreeElement(); if (textBlock.isEmpty()) return null; // if tree was just loaded above textBlock will be cleared by contentsLoaded final CharSequence chars = document.getCharsSequence(); final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY); if (data != null) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data); } final String oldPsiText = ApplicationManagerEx.getApplicationEx().isInternal() && !ApplicationManagerEx.getApplicationEx().isUnitTestMode() ? myTreeElementBeingReparsedSoItWontBeCollected.getText() : null; int startOffset; int endOffset; int lengthShift; if (file.getViewProvider().supportsIncrementalReparse(file.getLanguage())) { startOffset = textBlock.getStartOffset(); int psiEndOffset = textBlock.getPsiEndOffset(); endOffset = psiEndOffset; lengthShift = textBlock.getTextEndOffset() - psiEndOffset; } else { startOffset = 0; endOffset = document.getTextLength(); lengthShift = document.getTextLength() - myTreeElementBeingReparsedSoItWontBeCollected.getTextLength(); } assertBeforeCommit( document, file, textBlock, chars, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected); BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars, indicator); return new Processor<Document>() { @Override public boolean process(Document document) { ApplicationManager.getApplication().assertWriteAccessAllowed(); log( "Finishing", document, synchronously, document.getModificationStamp(), startDocModificationTimeStamp); // if (file.getModificationStamp() != startPsiModificationTimeStamp) return; // optimistic // locking failed if (document.getModificationStamp() != startDocModificationTimeStamp) { return false; // optimistic locking failed } try { textBlock.performAtomically( new Runnable() { @Override public void run() { CodeStyleManager.getInstance(file.getProject()) .performActionWithFormatterDisabled( new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { doActualPsiChange(file, diffLog); } } }); } }); assertAfterCommit( document, file, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected); } finally { textBlock.clear(); SmartPointerManagerImpl.synchronizePointers(file); } return true; } }; }