private void doSync(
      @NotNull final PsiTreeChangeEvent event,
      boolean force,
      @NotNull final DocSyncAction syncAction) {
    if (!toProcessPsiEvent()) return;
    final PsiFile psiFile = event.getFile();
    if (!(psiFile instanceof PsiFileEx) || !((PsiFileEx) psiFile).isContentsLoaded()) return;

    final DocumentEx document = getCachedDocument(psiFile, force);
    if (document == null) return;

    performAtomically(
        psiFile,
        new Runnable() {
          @Override
          public void run() {
            syncAction.syncDocument(document, (PsiTreeChangeEventImpl) event);
          }
        });

    final boolean insideTransaction = myTransactionsMap.containsKey(document);
    if (!insideTransaction) {
      document.setModificationStamp(psiFile.getViewProvider().getModificationStamp());
    }
  }
  @Override
  @Nullable
  public Document getDocument(@NotNull final VirtualFile file) {
    DocumentEx document = (DocumentEx) getCachedDocument(file);
    if (document == null) {
      if (file.isDirectory()
          || isBinaryWithoutDecompiler(file)
          || SingleRootFileViewProvider.isTooLargeForContentLoading(file)) {
        return null;
      }
      final CharSequence text = LoadTextUtil.loadText(file);

      synchronized (lock) {
        document = (DocumentEx) getCachedDocument(file);
        if (document != null) return document; // Double checking

        document = (DocumentEx) createDocument(text);
        document.setModificationStamp(file.getModificationStamp());
        final FileType fileType = file.getFileType();
        document.setReadOnly(!file.isWritable() || fileType.isBinary());
        file.putUserData(DOCUMENT_KEY, new WeakReference<Document>(document));
        document.putUserData(FILE_KEY, file);

        if (!(file instanceof LightVirtualFile
            || file.getFileSystem() instanceof DummyFileSystem)) {
          document.addDocumentListener(
              new DocumentAdapter() {
                @Override
                public void documentChanged(DocumentEvent e) {
                  final Document document = e.getDocument();
                  myUnsavedDocuments.add(document);
                  final Runnable currentCommand =
                      CommandProcessor.getInstance().getCurrentCommand();
                  Project project =
                      currentCommand == null
                          ? null
                          : CommandProcessor.getInstance().getCurrentCommandProject();
                  String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator();
                  document.putUserData(LINE_SEPARATOR_KEY, lineSeparator);

                  // avoid documents piling up during batch processing
                  if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) {
                    saveAllDocumentsLater();
                  }
                }
              });
        }
      }

      myMultiCaster.fileContentLoaded(file, document);
    }

    return document;
  }
  private void doSync(
      @NotNull final PsiTreeChangeEvent event,
      boolean force,
      @NotNull final DocSyncAction syncAction) {
    if (!toProcessPsiEvent()) return;
    final PsiFile psiFile = event.getFile();
    if (psiFile == null || psiFile.getNode() == null) return;

    final DocumentEx document = (DocumentEx) myPsiDocumentManager.getCachedDocument(psiFile);
    if (document == null || document instanceof DocumentWindow) return;
    if (!force && getTransaction(document) == null) {
      return;
    }

    TextBlock textBlock = TextBlock.get(psiFile);

    if (!textBlock.isEmpty()) {
      throw new IllegalStateException("Attempt to modify PSI for non-committed Document!");
    }

    textBlock.performAtomically(
        new Runnable() {
          @Override
          public void run() {
            syncAction.syncDocument(document, (PsiTreeChangeEventImpl) event);
          }
        });

    final boolean insideTransaction = myTransactionsMap.containsKey(document);
    if (!insideTransaction) {
      document.setModificationStamp(psiFile.getViewProvider().getModificationStamp());
      if (LOG.isDebugEnabled()) {
        PsiDocumentManagerBase.checkConsistency(psiFile, document);
      }
    }

    psiFile.getViewProvider().contentsSynchronized();
  }
  private void doSaveDocumentInWriteAction(@NotNull Document document, @NotNull VirtualFile file)
      throws IOException {
    if (!file.isValid()) {
      removeFromUnsaved(document);
      return;
    }

    if (!file.equals(getFile(document))) {
      registerDocument(document, file);
    }

    if (!isSaveNeeded(document, file)) {
      if (document instanceof DocumentEx) {
        ((DocumentEx) document).setModificationStamp(file.getModificationStamp());
      }
      removeFromUnsaved(document);
      updateModifiedProperty(file);
      return;
    }

    myMultiCaster.beforeDocumentSaving(document);

    LOG.assertTrue(file.isValid());

    String text = document.getText();
    String lineSeparator = getLineSeparator(document, file);
    if (!lineSeparator.equals("\n")) {
      text = StringUtil.convertLineSeparators(text, lineSeparator);
    }

    Project project = ProjectLocator.getInstance().guessProjectForFile(file);
    LoadTextUtil.write(project, file, this, text, document.getModificationStamp());

    myUnsavedDocuments.remove(document);
    LOG.assertTrue(!myUnsavedDocuments.contains(document));
    myTrailingSpacesStripper.clearLineModificationFlags(document);
  }
 @Override
 public void replaceText(@NotNull final CharSequence chars, final long newModificationStamp) {
   setText(chars);
   myDelegate.setModificationStamp(newModificationStamp);
 }
 @Override
 public void setModificationStamp(final long modificationStamp) {
   myDelegate.setModificationStamp(modificationStamp);
 }