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;
      }
    };
  }