@Override
  public void contentsChanged(VirtualFileEvent event) {
    if (event.isFromSave()) return;
    final VirtualFile file = event.getFile();
    final Document document = getCachedDocument(file);
    if (document == null) {
      myMultiCaster.fileWithNoDocumentChanged(file);
      return;
    }

    if (isBinaryWithDecompiler(file)) {
      myMultiCaster.fileWithNoDocumentChanged(
          file); // This will generate PSI event at FileManagerImpl
    }

    long documentStamp = document.getModificationStamp();
    long oldFileStamp = event.getOldModificationStamp();
    if (documentStamp != oldFileStamp) {
      LOG.info("reload from disk?");
      LOG.info("  documentStamp:" + documentStamp);
      LOG.info("  oldFileStamp:" + oldFileStamp);

      if (file.isValid() && askReloadFromDisk(file, document)) {
        reloadFromDisk(document);
      }
    } else {
      reloadFromDisk(document);
    }
  }
  @Override
  public void saveAllDocuments() {
    ApplicationManager.getApplication().assertIsDispatchThread();

    myMultiCaster.beforeAllDocumentsSaving();
    if (myUnsavedDocuments.isEmpty()) return;

    final Map<Document, IOException> failedToSave = new HashMap<Document, IOException>();
    final Set<Document> vetoed = new HashSet<Document>();
    while (true) {
      int count = 0;

      for (Document document : myUnsavedDocuments) {
        if (failedToSave.containsKey(document)) continue;
        if (vetoed.contains(document)) continue;
        try {
          doSaveDocument(document);
        } catch (IOException e) {
          //noinspection ThrowableResultOfMethodCallIgnored
          failedToSave.put(document, e);
        } catch (SaveVetoException e) {
          vetoed.add(document);
        }
        count++;
      }

      if (count == 0) break;
    }

    if (!failedToSave.isEmpty()) {
      handleErrorsOnSave(failedToSave);
    }
  }
  @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 boolean fireBeforeFileContentReload(final VirtualFile file, @NotNull Document document) {
    for (FileDocumentSynchronizationVetoer vetoer :
        Extensions.getExtensions(FileDocumentSynchronizationVetoer.EP_NAME)) {
      try {
        if (!vetoer.mayReloadFileContent(file, document)) {
          return false;
        }
      } catch (Exception e) {
        LOG.error(e);
      }
    }

    myMultiCaster.beforeFileContentReload(file, document);
    return true;
  }
  @Override
  public void reloadFromDisk(@NotNull final Document document) {
    ApplicationManager.getApplication().assertIsDispatchThread();

    final VirtualFile file = getFile(document);
    assert file != null;

    if (!fireBeforeFileContentReload(file, document)) {
      return;
    }

    final Project project = ProjectLocator.getInstance().guessProjectForFile(file);
    CommandProcessor.getInstance()
        .executeCommand(
            project,
            new Runnable() {
              @Override
              public void run() {
                ApplicationManager.getApplication()
                    .runWriteAction(
                        new ExternalChangeAction.ExternalDocumentChange(document, project) {
                          @Override
                          public void run() {
                            boolean wasWritable = document.isWritable();
                            DocumentEx documentEx = (DocumentEx) document;
                            documentEx.setReadOnly(false);
                            LoadTextUtil.setCharsetWasDetectedFromBytes(file, null);
                            documentEx.replaceText(
                                LoadTextUtil.loadText(file), file.getModificationStamp());
                            documentEx.setReadOnly(!wasWritable);
                          }
                        });
              }
            },
            UIBundle.message("file.cache.conflict.action"),
            null,
            UndoConfirmationPolicy.REQUEST_CONFIRMATION);

    myUnsavedDocuments.remove(document);

    myMultiCaster.fileContentReloaded(file, document);
  }
  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);
  }
 private void fireUnsavedDocumentsDropped() {
   myMultiCaster.unsavedDocumentsDropped();
 }