when file content is reloaded externally, clear all roots in the view provider (EA...
authorpeter <peter@jetbrains.com>
Wed, 29 Jul 2015 19:26:09 +0000 (21:26 +0200)
committerpeter <peter@jetbrains.com>
Wed, 29 Jul 2015 19:26:09 +0000 (21:26 +0200)
platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java
platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java
platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java
platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java
platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java
python/testSrc/com/jetbrains/python/PyStubsTest.java

index bf6acdf11bd8757e9cdbe43dee80dd5e4830bbab..68353cd0c47802646f258d14eff7035f94535741 100644 (file)
@@ -29,6 +29,7 @@ import com.intellij.psi.impl.source.PsiFileImpl;
 import com.intellij.psi.impl.source.tree.FileElement;
 import com.intellij.psi.templateLanguages.OuterLanguageElement;
 import com.intellij.util.ConcurrencyUtil;
+import com.intellij.util.NullableFunction;
 import com.intellij.util.ReflectionUtil;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
@@ -107,9 +108,20 @@ public abstract class MultiplePsiFilesPerDocumentFileViewProvider extends Single
     return myRoots.get(target);
   }
 
+  @Override
+  public List<PsiFile> getCachedPsiFiles() {
+    return ContainerUtil.mapNotNull(myRoots.keySet(), new NullableFunction<Language, PsiFile>() {
+      @Nullable
+      @Override
+      public PsiFile fun(Language language) {
+        return getCachedPsi(language);
+      }
+    });
+  }
+
   @NotNull
   @Override
-  public FileElement[] getKnownTreeRoots() {
+  public List<FileElement> getKnownTreeRoots() {
     List<FileElement> files = new ArrayList<FileElement>(myRoots.size());
     for (PsiFile file : myRoots.values()) {
       final FileElement treeElement = ((PsiFileImpl)file).getTreeElement();
@@ -118,7 +130,7 @@ public abstract class MultiplePsiFilesPerDocumentFileViewProvider extends Single
       }
     }
 
-    return files.toArray(new FileElement[files.size()]);
+    return files;
   }
 
   @TestOnly
index c2ef19131a90dbdc30b640d21d1eab250e5ba357..27278aaeb6a9da7b5575c1302efbac47a5c3bb65 100644 (file)
@@ -39,8 +39,7 @@ import com.intellij.openapi.vfs.NonPhysicalFileSystem;
 import com.intellij.openapi.vfs.PersistentFSConstants;
 import com.intellij.openapi.vfs.VFileProperty;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.impl.PsiManagerEx;
-import com.intellij.psi.impl.PsiManagerImpl;
+import com.intellij.psi.impl.*;
 import com.intellij.psi.impl.file.PsiBinaryFileImpl;
 import com.intellij.psi.impl.file.PsiLargeFileImpl;
 import com.intellij.psi.impl.file.impl.FileManager;
@@ -198,17 +197,66 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
 
   @Override
   public void contentsSynchronized() {
-    if (!(myContent instanceof PsiFileContent)) return;
-    setContent(new VirtualFileContent());
+    if (myContent instanceof PsiFileContent) {
+      setContent(new VirtualFileContent());
+    }
+    checkLengthConsistency();
   }
 
   public void beforeDocumentChanged(@Nullable PsiFile psiCause) {
     PsiFile psiFile = psiCause != null ? psiCause : getPsi(getBaseLanguage());
     if (psiFile instanceof PsiFileImpl) {
       setContent(new PsiFileContent((PsiFileImpl)psiFile, psiCause == null ? getModificationStamp() : LocalTimeCounter.currentTime()));
+      checkLengthConsistency();
     }
   }
 
+  public final void onContentReload() {
+    List<PsiFile> files = getCachedPsiFiles();
+    List<PsiTreeChangeEventImpl> events = ContainerUtil.newArrayList();
+    List<PsiTreeChangeEventImpl> genericEvents = ContainerUtil.newArrayList();
+    for (PsiFile file : files) {
+      genericEvents.add(createChildrenChangeEvent(file, true));
+      events.add(createChildrenChangeEvent(file, false));
+    }
+
+    beforeContentsSynchronized();
+
+    for (PsiTreeChangeEventImpl event : genericEvents) {
+      ((PsiManagerImpl)getManager()).beforeChildrenChange(event);
+    }
+    for (PsiTreeChangeEventImpl event : events) {
+      ((PsiManagerImpl)getManager()).beforeChildrenChange(event);
+    }
+
+    for (PsiFile psiFile : files) {
+      if (psiFile instanceof PsiFileEx) {
+        ((PsiFileEx)psiFile).onContentReload();
+      }
+    }
+
+    for (PsiTreeChangeEventImpl event : events) {
+      ((PsiManagerImpl)getManager()).childrenChanged(event);
+    }
+    for (PsiTreeChangeEventImpl event : genericEvents) {
+      ((PsiManagerImpl)getManager()).childrenChanged(event);
+    }
+
+    contentsSynchronized();
+  }
+
+  private PsiTreeChangeEventImpl createChildrenChangeEvent(PsiFile file, boolean generic) {
+    PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myManager);
+    event.setParent(file);
+    event.setFile(file);
+    event.setGenericChange(generic);
+    if (file instanceof PsiFileImpl && ((PsiFileImpl)file).isContentsLoaded()) {
+      event.setOffset(0);
+      event.setOldLength(file.getTextLength());
+    }
+    return event;
+  }
+
   @Override
   public void rootChanged(@NotNull PsiFile psiFile) {
     if (psiFile instanceof PsiFileImpl && ((PsiFileImpl)psiFile).isContentsLoaded()) {
@@ -242,12 +290,16 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
     return file == PsiUtilCore.NULL_PSI_FILE ? null : file;
   }
 
+  public List<PsiFile> getCachedPsiFiles() {
+    return ContainerUtil.createMaybeSingletonList(getCachedPsi(myBaseLanguage));
+  }
+
   @NotNull
-  public FileElement[] getKnownTreeRoots() {
+  public List<FileElement> getKnownTreeRoots() {
     PsiFile psiFile = getCachedPsi(myBaseLanguage);
-    if (!(psiFile instanceof PsiFileImpl)) return FileElement.EMPTY_ARRAY;
-    if (((PsiFileImpl)psiFile).getTreeElement() == null) return FileElement.EMPTY_ARRAY;
-    return new FileElement[]{(FileElement)psiFile.getNode()};
+    if (!(psiFile instanceof PsiFileImpl)) return Collections.emptyList();
+    FileElement element = ((PsiFileImpl)psiFile).getTreeElement();
+    return ContainerUtil.createMaybeSingletonList(element);
   }
 
   private PsiFile createFile() {
@@ -485,18 +537,27 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
   }
 
   private void setContent(@NotNull Content content) {
-    Content prevContent = myContent;
     myContent = content;
-    if (prevContent instanceof PsiFileContent && content instanceof VirtualFileContent) {
-      int fileLength = content.getText().length();
-      for (FileElement fileElement : getKnownTreeRoots()) {
-        int nodeLength = fileElement.getTextLength();
-        if (nodeLength != fileLength) {
-          LOG.error("Inconsistent " + fileElement.getElementType() + " tree in " + this + "; nodeLength=" + nodeLength + "; fileLength=" + fileLength);
-        }
+  }
+
+  private void checkLengthConsistency() {
+    Document document = getCachedDocument();
+    if (document != null &&
+        ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myManager.getProject())).getSynchronizer().isInSynchronization(document)) {
+      return;
+    }
+
+    List<FileElement> knownTreeRoots = getKnownTreeRoots();
+    if (knownTreeRoots.isEmpty()) return;
+
+    int fileLength = myContent.getText().length();
+    for (FileElement fileElement : knownTreeRoots) {
+      int nodeLength = fileElement.getTextLength();
+      if (nodeLength != fileLength) {
+        // exceptions here should be assigned to peter
+        LOG.error("Inconsistent " + fileElement.getElementType() + " tree in " + this + "; nodeLength=" + nodeLength + "; fileLength=" + fileLength);
       }
     }
-    
   }
 
   @NonNls
index 334cb96f1d161a98193a6b466197952b17c6a79e..619d8137771a84199b7c5df9217c41d77bd11855 100644 (file)
@@ -750,19 +750,12 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
     ApplicationManager.getApplication().runWriteAction(new ExternalChangeAction() {
       @Override
       public void run() {
-        psiFile.getViewProvider().beforeContentsSynchronized();
-        synchronized (PsiLock.LOCK) {
-          final int oldLength = prevInfo.myFrozen.getTextLength();
-          PsiManagerImpl manager = (PsiManagerImpl)psiFile.getManager();
-          BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, true);
-          BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, false);
-          if (psiFile instanceof PsiFileImpl) {
-            ((PsiFileImpl)psiFile).onContentReload();
-          }
-          BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, false);
-          BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, true);
+        FileViewProvider viewProvider = psiFile.getViewProvider();
+        if (viewProvider instanceof SingleRootFileViewProvider) {
+          ((SingleRootFileViewProvider)viewProvider).onContentReload();
+        } else {
+          LOG.error("Invalid view provider: " + viewProvider + " of " + viewProvider.getClass());
         }
-        psiFile.getViewProvider().contentsSynchronized();
       }
     });
   }
index 419218f420c295a847d1453d348e5c70fe9a1560..402d5bf2b53abf5802ae31044035bbec232301f8 100644 (file)
@@ -599,20 +599,12 @@ public class FileManagerImpl implements FileManager {
         return;
       }
 
-      PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myManager);
-      event.setParent(file);
-      event.setFile(file);
-      if (file instanceof PsiFileImpl && ((PsiFileImpl)file).isContentsLoaded()) {
-        event.setOffset(0);
-        event.setOldLength(file.getTextLength());
-      }
-      myManager.beforeChildrenChange(event);
-
-      if (file instanceof PsiFileEx) {
-        ((PsiFileEx)file).onContentReload();
+      FileViewProvider viewProvider = file.getViewProvider();
+      if (viewProvider instanceof SingleRootFileViewProvider) {
+        ((SingleRootFileViewProvider)viewProvider).onContentReload();
+      } else {
+        LOG.error("Invalid view provider: " + viewProvider + " of " + viewProvider.getClass());
       }
-
-      myManager.childrenChanged(event);
     }
   }
 }
index adf8724f0b1477ff3b09819f641949d9da3ddb32..e3477a589b108ccf34bb9ae5ee5ddcf3b5414a4f 100644 (file)
@@ -349,28 +349,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
     return treeElement;
   }
 
-  public void unloadContent() {
-    ApplicationManager.getApplication().assertWriteAccessAllowed();
-    clearCaches();
-    myViewProvider.beforeContentsSynchronized();
-    synchronized (PsiLock.LOCK) {
-      FileElement treeElement = derefTreeElement();
-      DebugUtil.startPsiModification("unloadContent");
-      try {
-        if (treeElement != null) {
-          myTreeElementPointer = null;
-          treeElement.detachFromFile();
-          DebugUtil.onInvalidated(treeElement);
-        }
-        clearStub("unloadContent");
-      }
-      finally {
-        DebugUtil.finishPsiModification();
-      }
-    }
-    myViewProvider.contentsSynchronized();
-  }
-
   private void clearStub(@NotNull String reason) {
     StubTree stubHolder = SoftReference.dereference(myStub);
     if (stubHolder != null) {
@@ -633,10 +611,22 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
 
   @Override
   public void onContentReload() {
-    subtreeChanged(); // important! otherwise cached information is not released
-    if (isContentsLoaded()) {
-      unloadContent();
+    ApplicationManager.getApplication().assertWriteAccessAllowed();
+
+    FileElement treeElement = derefTreeElement();
+    DebugUtil.startPsiModification("onContentReload");
+    try {
+      if (treeElement != null) {
+        myTreeElementPointer = null;
+        treeElement.detachFromFile();
+        DebugUtil.onInvalidated(treeElement);
+      }
+      clearStub("onContentReload");
+    }
+    finally {
+      DebugUtil.finishPsiModification();
     }
+    clearCaches();
   }
 
   @Nullable
index 7120957f31b87234ee9d63b2b9d97a7ea67a9301..d23ecf1736f165c7bb6dd110d9cad9c0e3a08584 100644 (file)
@@ -21,15 +21,13 @@ import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.project.DumbServiceImpl;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiDocumentManager;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiManager;
+import com.intellij.psi.*;
 import com.intellij.psi.impl.source.PsiFileImpl;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.stubs.StubElement;
 import com.intellij.psi.stubs.StubUpdatingIndex;
 import com.intellij.psi.util.QualifiedName;
+import com.intellij.testFramework.PlatformTestUtil;
 import com.intellij.testFramework.TestDataPath;
 import com.intellij.util.indexing.FileBasedIndex;
 import com.jetbrains.python.fixtures.PyTestCase;
@@ -215,20 +213,15 @@ public class PyStubsTest extends PyTestCase {
     StubElement fileStub = fileImpl.getStub();
     assertNull("There should be no stub if file holds tree element", fileStub);
 
-    FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, myFixture.getProject(), null);
     new WriteCommandAction(myFixture.getProject(), fileImpl) {
       @Override
       protected void run(@NotNull Result result) throws Throwable {
-        fileImpl.unloadContent();
+        ((SingleRootFileViewProvider)fileImpl.getViewProvider()).onContentReload();
       }
     }.execute();
-    assertNull(fileImpl.getTreeElement()); // Test unload successed.
+    assertNull(fileImpl.getTreeElement()); // Test unload succeeded.
 
-    fileStub = fileImpl.getStub();
-    assertNotNull("After tree element have been unloaded we must be able to create updated stub", fileStub);
-
-    final PyClassStub newclassstub = (PyClassStub)fileStub.getChildrenStubs().get(0);
-    assertEquals("RenamedClass", newclassstub.getName());
+    assertEquals("RenamedClass", fileImpl.getTopLevelClasses().get(0).getName());
   }
 
   public void testImportStatement() {