diff --git a/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java index bf6acdf11bd8..68353cd0c478 100644 --- a/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java @@ -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 getCachedPsiFiles() { + return ContainerUtil.mapNotNull(myRoots.keySet(), new NullableFunction() { + @Nullable + @Override + public PsiFile fun(Language language) { + return getCachedPsi(language); + } + }); + } + @NotNull @Override - public FileElement[] getKnownTreeRoots() { + public List getKnownTreeRoots() { List files = new ArrayList(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 diff --git a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java index c2ef19131a90..27278aaeb6a9 100644 --- a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java @@ -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 files = getCachedPsiFiles(); + List events = ContainerUtil.newArrayList(); + List 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 getCachedPsiFiles() { + return ContainerUtil.createMaybeSingletonList(getCachedPsi(myBaseLanguage)); + } + @NotNull - public FileElement[] getKnownTreeRoots() { + public List 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 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 diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index 334cb96f1d16..619d8137771a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -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(); } }); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java index 419218f420c2..402d5bf2b53a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java @@ -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()); + FileViewProvider viewProvider = file.getViewProvider(); + if (viewProvider instanceof SingleRootFileViewProvider) { + ((SingleRootFileViewProvider)viewProvider).onContentReload(); + } else { + LOG.error("Invalid view provider: " + viewProvider + " of " + viewProvider.getClass()); } - myManager.beforeChildrenChange(event); - - if (file instanceof PsiFileEx) { - ((PsiFileEx)file).onContentReload(); - } - - myManager.childrenChanged(event); } } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index adf8724f0b14..e3477a589b10 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -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 diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 7120957f31b8..d23ecf1736f1 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -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() {