@Override
  public void fileDeleted(@NotNull final VirtualFileEvent event) {
    final VirtualFile vFile = event.getFile();

    VirtualFile parent = event.getParent();
    final PsiDirectory parentDir = getCachedDirectory(parent);

    final PsiFile psiFile = myFileManager.getCachedPsiFileInner(vFile);
    if (psiFile != null) {
      myFileManager.setViewProvider(vFile, null);

      if (parentDir != null) {
        ApplicationManager.getApplication()
            .runWriteAction(
                new ExternalChangeAction() {
                  @Override
                  public void run() {
                    PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);
                    treeEvent.setParent(parentDir);
                    treeEvent.setChild(psiFile);
                    myManager.childRemoved(treeEvent);
                  }
                });
      } else if (parent != null) {
        handleVfsChangeWithoutPsi(parent);
      }
    } else {
      final PsiDirectory psiDir = myFileManager.getCachedDirectory(vFile);
      if (psiDir != null) {
        myFileManager.removeInvalidFilesAndDirs(false);

        if (parentDir != null) {
          ApplicationManager.getApplication()
              .runWriteAction(
                  new ExternalChangeAction() {
                    @Override
                    public void run() {
                      PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);
                      treeEvent.setParent(parentDir);
                      treeEvent.setChild(psiDir);
                      myManager.childRemoved(treeEvent);
                    }
                  });
        }
      } else if (parent != null) {
        handleVfsChangeWithoutPsi(parent);
      }
    }
  }
 private void handleVfsChangeWithoutPsi(@NotNull VirtualFile vFile) {
   if (!myReportedUnloadedPsiChange && isInRootModel(vFile)) {
     PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myManager);
     myFileManager.firePropertyChangedForUnloadedPsi(event, vFile);
     myReportedUnloadedPsiChange = true;
   }
 }
  @Override
  public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
    final VirtualFile vFile = event.getFile();

    final PsiDirectory oldParentDir = myFileManager.findDirectory(event.getOldParent());
    final PsiDirectory newParentDir = myFileManager.findDirectory(event.getNewParent());
    if (oldParentDir == null && newParentDir == null) return;
    if (myFileTypeManager.isFileIgnored(vFile)) return;

    ApplicationManager.getApplication()
        .runWriteAction(
            new ExternalChangeAction() {
              @Override
              public void run() {
                PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);

                boolean isExcluded =
                    vFile.isDirectory()
                        && Registry.is("ide.hide.excluded.files")
                        && myProjectRootManager.getFileIndex().isExcluded(vFile);
                if (oldParentDir != null && !isExcluded) {
                  if (newParentDir != null) {
                    treeEvent.setOldParent(oldParentDir);
                    treeEvent.setNewParent(newParentDir);
                    if (vFile.isDirectory()) {
                      PsiDirectory psiDir = myFileManager.findDirectory(vFile);
                      treeEvent.setChild(psiDir);
                    } else {
                      PsiFile psiFile = myFileManager.findFile(vFile);
                      treeEvent.setChild(psiFile);
                    }
                    myManager.beforeChildMovement(treeEvent);
                  } else {
                    treeEvent.setParent(oldParentDir);
                    if (vFile.isDirectory()) {
                      PsiDirectory psiDir = myFileManager.findDirectory(vFile);
                      treeEvent.setChild(psiDir);
                    } else {
                      PsiFile psiFile = myFileManager.findFile(vFile);
                      treeEvent.setChild(psiFile);
                    }
                    myManager.beforeChildRemoval(treeEvent);
                  }
                } else {
                  LOG.assertTrue(newParentDir != null); // checked above
                  treeEvent.setParent(newParentDir);
                  myManager.beforeChildAddition(treeEvent);
                }
              }
            });
  }
  @Override
  public void fileMoved(@NotNull VirtualFileMoveEvent event) {
    // let PushedFilePropertiesUpdater process all pending vfs events and update file properties
    // before we issue PSI events
    PushedFilePropertiesUpdater.getInstance(myProject).processPendingEvents();

    final VirtualFile vFile = event.getFile();

    final PsiDirectory oldParentDir = myFileManager.findDirectory(event.getOldParent());
    final PsiDirectory newParentDir = myFileManager.findDirectory(event.getNewParent());
    if (oldParentDir == null && newParentDir == null) return;

    final PsiElement oldElement =
        vFile.isDirectory()
            ? myFileManager.getCachedDirectory(vFile)
            : myFileManager.getCachedPsiFileInner(vFile);
    myFileManager.removeInvalidFilesAndDirs(true);
    final PsiElement newElement;
    final FileViewProvider newViewProvider;
    if (!vFile.isDirectory()) {
      newViewProvider = myFileManager.createFileViewProvider(vFile, true);
      newElement = newViewProvider.getPsi(myFileManager.findViewProvider(vFile).getBaseLanguage());
    } else {
      newElement = myFileManager.findDirectory(vFile);
      newViewProvider = null;
    }

    if (oldElement == null && newElement == null) return;

    ApplicationManager.getApplication()
        .runWriteAction(
            new ExternalChangeAction() {
              @Override
              public void run() {
                PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);
                if (oldElement == null) {
                  myFileManager.setViewProvider(vFile, newViewProvider);
                  treeEvent.setParent(newParentDir);
                  treeEvent.setChild(newElement);
                  myManager.childAdded(treeEvent);
                } else {
                  if (newElement == null) {
                    myFileManager.setViewProvider(vFile, null);
                    treeEvent.setParent(oldParentDir);
                    treeEvent.setChild(oldElement);
                    myManager.childRemoved(treeEvent);
                  } else {
                    if (newElement instanceof PsiDirectory
                        || FileManagerImpl.areViewProvidersEquivalent(
                            newViewProvider, ((PsiFile) oldElement).getViewProvider())) {
                      treeEvent.setOldParent(oldParentDir);
                      treeEvent.setNewParent(newParentDir);
                      treeEvent.setChild(oldElement);
                      myManager.childMoved(treeEvent);
                    } else {
                      myFileManager.setViewProvider(vFile, newViewProvider);
                      PsiTreeChangeEventImpl treeRemoveEvent =
                          new PsiTreeChangeEventImpl(myManager);
                      treeRemoveEvent.setParent(oldParentDir);
                      treeRemoveEvent.setChild(oldElement);
                      myManager.childRemoved(treeRemoveEvent);
                      PsiTreeChangeEventImpl treeAddEvent = new PsiTreeChangeEventImpl(myManager);
                      treeAddEvent.setParent(newParentDir);
                      treeAddEvent.setChild(newElement);
                      myManager.childAdded(treeAddEvent);
                    }
                  }
                }
              }
            });
  }
  @Override
  public void propertyChanged(@NotNull final VirtualFilePropertyEvent event) {
    final String propertyName = event.getPropertyName();
    final VirtualFile vFile = event.getFile();

    final FileViewProvider oldFileViewProvider = myFileManager.findCachedViewProvider(vFile);
    final PsiFile oldPsiFile;
    if (oldFileViewProvider instanceof SingleRootFileViewProvider) {
      oldPsiFile =
          ((SingleRootFileViewProvider) oldFileViewProvider)
              .getCachedPsi(oldFileViewProvider.getBaseLanguage());
    } else {
      oldPsiFile = null;
    }

    VirtualFile parent = vFile.getParent();
    final PsiDirectory parentDir =
        oldPsiFile != null && parent != null
            ? myFileManager.findDirectory(parent)
            : getCachedDirectory(parent);

    if (oldFileViewProvider
            != null // there is no need to rebuild if there were no PSI in the first place
        && FileContentUtilCore.FORCE_RELOAD_REQUESTOR.equals(event.getRequestor())) {
      myFileManager.forceReload(vFile);
      return;
    }

    // do not suppress reparse request for light files
    if (parentDir == null) {
      boolean fire = VirtualFile.PROP_NAME.equals(propertyName) && vFile.isDirectory();
      if (fire) {
        PsiDirectory psiDir = myFileManager.getCachedDirectory(vFile);
        fire = psiDir != null;
      }
      if (!fire && !VirtualFile.PROP_WRITABLE.equals(propertyName)) {
        handleVfsChangeWithoutPsi(vFile);
        return;
      }
    }

    ((SmartPointerManagerImpl) SmartPointerManager.getInstance(myManager.getProject()))
        .fastenBelts(vFile, 0, null);
    ApplicationManager.getApplication()
        .runWriteAction(
            new ExternalChangeAction() {
              @Override
              public void run() {
                PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);
                treeEvent.setParent(parentDir);

                if (VirtualFile.PROP_NAME.equals(propertyName)) {
                  if (vFile.isDirectory()) {
                    PsiDirectory psiDir = myFileManager.getCachedDirectory(vFile);
                    if (psiDir != null) {
                      if (myFileTypeManager.isFileIgnored(vFile)) {
                        myFileManager.removeFilesAndDirsRecursively(vFile);

                        treeEvent.setChild(psiDir);
                        myManager.childRemoved(treeEvent);
                      } else {
                        treeEvent.setElement(psiDir);
                        treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_DIRECTORY_NAME);
                        treeEvent.setOldValue(event.getOldValue());
                        treeEvent.setNewValue(event.getNewValue());
                        myManager.propertyChanged(treeEvent);
                      }
                    } else {
                      PsiDirectory psiDir1 = myFileManager.findDirectory(vFile);
                      if (psiDir1 != null) {
                        treeEvent.setChild(psiDir1);
                        myManager.childAdded(treeEvent);
                      }
                    }
                  } else {
                    final FileViewProvider fileViewProvider =
                        myFileManager.createFileViewProvider(vFile, true);
                    final PsiFile newPsiFile =
                        fileViewProvider.getPsi(fileViewProvider.getBaseLanguage());
                    if (oldPsiFile != null) {
                      if (newPsiFile == null) {
                        myFileManager.setViewProvider(vFile, null);

                        treeEvent.setChild(oldPsiFile);
                        myManager.childRemoved(treeEvent);
                      } else if (!FileManagerImpl.areViewProvidersEquivalent(
                          fileViewProvider, oldFileViewProvider)) {
                        myFileManager.setViewProvider(vFile, fileViewProvider);

                        treeEvent.setOldChild(oldPsiFile);
                        treeEvent.setNewChild(newPsiFile);
                        myManager.childReplaced(treeEvent);
                      } else {
                        if (oldPsiFile instanceof PsiFileImpl) {
                          ((PsiFileImpl) oldPsiFile).clearCaches();
                        }
                        treeEvent.setElement(oldPsiFile);
                        treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_FILE_NAME);
                        treeEvent.setOldValue(event.getOldValue());
                        treeEvent.setNewValue(event.getNewValue());
                        myManager.propertyChanged(treeEvent);
                      }
                    } else if (newPsiFile != null) {
                      myFileManager.setViewProvider(vFile, fileViewProvider);
                      if (parentDir != null) {
                        treeEvent.setChild(newPsiFile);
                        myManager.childAdded(treeEvent);
                      }
                    }
                  }
                } else if (VirtualFile.PROP_WRITABLE.equals(propertyName)) {
                  if (oldPsiFile == null) return;

                  treeEvent.setElement(oldPsiFile);
                  treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_WRITABLE);
                  treeEvent.setOldValue(event.getOldValue());
                  treeEvent.setNewValue(event.getNewValue());
                  myManager.propertyChanged(treeEvent);
                } else if (VirtualFile.PROP_ENCODING.equals(propertyName)) {
                  if (oldPsiFile == null) return;

                  treeEvent.setElement(oldPsiFile);
                  treeEvent.setPropertyName(VirtualFile.PROP_ENCODING);
                  treeEvent.setOldValue(event.getOldValue());
                  treeEvent.setNewValue(event.getNewValue());
                  myManager.propertyChanged(treeEvent);
                }
              }
            });
  }
  @Override
  public void beforePropertyChange(@NotNull final VirtualFilePropertyEvent event) {
    final VirtualFile vFile = event.getFile();
    final String propertyName = event.getPropertyName();

    final FileViewProvider viewProvider = myFileManager.findCachedViewProvider(vFile);

    VirtualFile parent = vFile.getParent();
    final PsiDirectory parentDir =
        viewProvider != null && parent != null
            ? myFileManager.findDirectory(parent)
            : getCachedDirectory(parent);
    if (parent != null && parentDir == null)
      return; // do not notifyListeners event if parent directory was never accessed via PSI

    ApplicationManager.getApplication()
        .runWriteAction(
            new ExternalChangeAction() {
              @Override
              public void run() {
                PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager);
                treeEvent.setParent(parentDir);

                if (VirtualFile.PROP_NAME.equals(propertyName)) {
                  final String newName = (String) event.getNewValue();

                  if (parentDir == null) return;

                  if (vFile.isDirectory()) {
                    PsiDirectory psiDir = myFileManager.findDirectory(vFile);
                    if (psiDir != null) {
                      if (!myFileTypeManager.isFileIgnored(newName)) {
                        treeEvent.setChild(psiDir);
                        treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_DIRECTORY_NAME);
                        treeEvent.setOldValue(vFile.getName());
                        treeEvent.setNewValue(newName);
                        myManager.beforePropertyChange(treeEvent);
                      } else {
                        treeEvent.setChild(psiDir);
                        myManager.beforeChildRemoval(treeEvent);
                      }
                    } else {
                      if ((!Registry.is("ide.hide.excluded.files") || !isExcludeRoot(vFile))
                          && !myFileTypeManager.isFileIgnored(newName)) {
                        myManager.beforeChildAddition(treeEvent);
                      }
                    }
                  } else {
                    final FileViewProvider viewProvider = myFileManager.findViewProvider(vFile);
                    PsiFile psiFile = viewProvider.getPsi(viewProvider.getBaseLanguage());
                    PsiFile psiFile1 = createFileCopyWithNewName(vFile, newName);

                    if (psiFile != null) {
                      if (psiFile1 == null) {
                        treeEvent.setChild(psiFile);
                        myManager.beforeChildRemoval(treeEvent);
                      } else if (!psiFile1.getClass().equals(psiFile.getClass())) {
                        treeEvent.setOldChild(psiFile);
                        myManager.beforeChildReplacement(treeEvent);
                      } else {
                        treeEvent.setChild(psiFile);
                        treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_FILE_NAME);
                        treeEvent.setOldValue(vFile.getName());
                        treeEvent.setNewValue(newName);
                        myManager.beforePropertyChange(treeEvent);
                      }
                    } else {
                      if (psiFile1 != null) {
                        myManager.beforeChildAddition(treeEvent);
                      }
                    }
                  }
                } else if (VirtualFile.PROP_WRITABLE.equals(propertyName)) {
                  PsiFile psiFile = myFileManager.getCachedPsiFileInner(vFile);
                  if (psiFile == null) return;

                  treeEvent.setElement(psiFile);
                  treeEvent.setPropertyName(PsiTreeChangeEvent.PROP_WRITABLE);
                  treeEvent.setOldValue(event.getOldValue());
                  treeEvent.setNewValue(event.getNewValue());
                  myManager.beforePropertyChange(treeEvent);
                }
              }
            });
  }
 @Nullable
 private PsiDirectory getCachedDirectory(VirtualFile parent) {
   return parent == null ? null : myFileManager.getCachedDirectory(parent);
 }