private static void assertAfterCommit( Document document, final PsiFile file, String oldPsiText, FileElement myTreeElementBeingReparsedSoItWontBeCollected) { if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { final String documentText = document.getText(); if (ApplicationManagerEx.getApplicationEx().isInternal()) { String fileText = file.getText(); LOG.error( "commitDocument left PSI inconsistent; file len=" + myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() + "; doc len=" + document.getTextLength() + "; doc.getText() == file.getText(): " + Comparing.equal(fileText, documentText) + ";\n file psi text=" + fileText + ";\n doc text=" + documentText + ";\n old psi file text=" + oldPsiText); } else { LOG.error("commitDocument left PSI inconsistent: " + file); } file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); try { BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange( file, 0, documentText.length(), 0, documentText, new ProgressIndicatorBase()); CodeStyleManager.getInstance(file.getProject()) .performActionWithFormatterDisabled( new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { doActualPsiChange(file, diffLog); } } }); if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { LOG.error("PSI is broken beyond repair in: " + file); } } finally { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); } } }
@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; } }; }