diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java index 90509b7f4ab3..a8db86f56b14 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java @@ -35,15 +35,13 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ConcurrentList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.URLUtil; +import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; import java.util.function.Predicate; /** @@ -189,15 +187,17 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme assert !myDisposed; dropCaches(); - for (final VirtualFilePointer pointer : that.getList()) { - myList.addIfAbsent(duplicate(pointer)); - } - for (VirtualFilePointer jarDirectory : ((VirtualFilePointerContainerImpl)that).myJarDirectories) { - myJarDirectories.add(duplicate(jarDirectory)); - } - for (VirtualFilePointer jarRecursiveDirectory : ((VirtualFilePointerContainerImpl)that).myJarRecursiveDirectories) { - myJarRecursiveDirectories.add(duplicate(jarRecursiveDirectory)); + Set set = new THashSet<>(myList); + List thatList = that.getList(); + List toAddList = new ArrayList<>(thatList.size()); + for (final VirtualFilePointer pointer : thatList) { + if (!set.contains(pointer)) toAddList.add(duplicate(pointer)); } + myList.addAll(toAddList); + List jarDups = ContainerUtil.map(((VirtualFilePointerContainerImpl)that).myJarDirectories, this::duplicate); + List jarRecursiveDups = ContainerUtil.map(((VirtualFilePointerContainerImpl)that).myJarRecursiveDirectories, this::duplicate); + myJarDirectories.addAll(jarDups); + myJarRecursiveDirectories.addAll(jarRecursiveDups); } private void dropCaches() { @@ -334,6 +334,7 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme return myList.size()+myJarDirectories.size()+myJarRecursiveDirectories.size(); } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof VirtualFilePointerContainerImpl)) return false; @@ -345,6 +346,7 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme myJarRecursiveDirectories.equals(impl.myJarRecursiveDirectories); } + @Override public int hashCode() { return myList.hashCode(); } @@ -384,15 +386,11 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme public VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) { assert !myDisposed; VirtualFilePointerContainerImpl clone = (VirtualFilePointerContainerImpl)myVirtualFilePointerManager.createContainer(parent, listener); - for (VirtualFilePointer pointer : myList) { - clone.add(pointer.getUrl()); - } - for (VirtualFilePointer jarDirectoryPtr : myJarDirectories) { - clone.addJarDirectory(jarDirectoryPtr.getUrl(), false); - } - for (VirtualFilePointer jarDirectoryPtr : myJarRecursiveDirectories) { - clone.addJarDirectory(jarDirectoryPtr.getUrl(), true); - } + + List toAdd = ContainerUtil.map(myList, p -> clone.create(p.getUrl())); + clone.myList.addAll(toAdd); + clone.addAllJarDirectories(ContainerUtil.map(myJarDirectories, VirtualFilePointer::getUrl), false); + clone.addAllJarDirectories(ContainerUtil.map(myJarRecursiveDirectories, VirtualFilePointer::getUrl), true); return clone; } @@ -413,6 +411,23 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme dropCaches(); } + /** optimization: faster than calling {@link #addJarDirectory(String, boolean)} one by one */ + public void addAllJarDirectories(@NotNull Collection directoryUrls, boolean recursively) { + if (directoryUrls.isEmpty()) return; + Set jars = new THashSet<>(recursively ? myJarRecursiveDirectories : myJarDirectories); + Set list = new THashSet<>(myList); + List toAddJars = new ArrayList<>(directoryUrls.size()); + List toAddList = new ArrayList<>(directoryUrls.size()); + directoryUrls.forEach(url -> { + VirtualFilePointer pointer = myVirtualFilePointerManager.createDirectoryPointer(url, recursively, myParent, myListener); + if (!jars.contains(pointer)) toAddJars.add(pointer); + if (!list.contains(pointer)) toAddList.add(pointer); + }); + (recursively ? myJarRecursiveDirectories : myJarDirectories).addAll(toAddJars); + myList.addAll(toAddList); // hack. jar directories need to be contained in class roots too (for externalization compatibility) but be ignored in getFiles() + dropCaches(); + } + @Override public boolean removeJarDirectory(@NotNull String directoryUrl) { dropCaches(); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java index 3b4d7a62b7d1..a74334679abd 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java @@ -27,6 +27,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.ex.VirtualFileManagerAdapter; +import com.intellij.openapi.vfs.impl.VirtualFilePointerContainerImpl; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; @@ -44,6 +45,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Collection; import java.util.Set; /** @@ -184,26 +186,32 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen ApplicationManager.getApplication().assertIsDispatchThread(); if (myProject.isDefault()) return null; - final Set recursive = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); - final Set flat = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); + final Set dirUrls = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); + final Set recursiveDirs = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); + final Set recursiveDirUrls = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); + final Set files = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); final String projectFilePath = myProject.getProjectFilePath(); final File projectDirFile = projectFilePath == null ? null : new File(projectFilePath).getParentFile(); if (projectDirFile != null && projectDirFile.getName().equals(Project.DIRECTORY_STORE_FOLDER)) { - recursive.add(projectDirFile.getAbsolutePath()); + dirUrls.add(VfsUtilCore.pathToUrl(projectDirFile.getAbsolutePath())); } else { - flat.add(projectFilePath); + files.add(projectFilePath); // may be not existing yet - ContainerUtil.addIfNotNull(flat, ProjectKt.getStateStore(myProject).getWorkspaceFilePath()); + ContainerUtil.addIfNotNull(files, ProjectKt.getStateStore(myProject).getWorkspaceFilePath()); } for (AdditionalLibraryRootsProvider extension : AdditionalLibraryRootsProvider.EP_NAME.getExtensions()) { - recursive.addAll(ContainerUtil.map(extension.getRootsToWatch(myProject), VirtualFile::getPath)); + Collection toWatch = extension.getRootsToWatch(myProject); + recursiveDirs.addAll(ContainerUtil.map(toWatch, VirtualFile::getPath)); + recursiveDirUrls.addAll(ContainerUtil.map(toWatch, VirtualFile::getUrl)); } for (WatchedRootsProvider extension : WatchedRootsProvider.EP_NAME.getExtensions(myProject)) { - recursive.addAll(extension.getRootsToWatch()); + Set toWatch = extension.getRootsToWatch(); + recursiveDirs.addAll(toWatch); + recursiveDirUrls.addAll(ContainerUtil.map(toWatch, p->VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(p)))); } Disposable oldDisposable = myRootPointersDisposable; @@ -211,14 +219,16 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen Disposer.register(this, myRootPointersDisposable); // create container with these urls with the sole purpose to get events to getRootsValidityChangedListener() when these roots change VirtualFilePointerContainer container = VirtualFilePointerManager.getInstance().createContainer(myRootPointersDisposable, getRootsValidityChangedListener()); - recursive.forEach(path -> container.addJarDirectory(VfsUtilCore.pathToUrl(path), true)); - flat.forEach(path -> container.add(VfsUtilCore.pathToUrl(path))); + + ((VirtualFilePointerContainerImpl)container).addAllJarDirectories(recursiveDirUrls, true); + ((VirtualFilePointerContainerImpl)container).addAllJarDirectories(dirUrls, false); + files.forEach(path -> container.add(VfsUtilCore.pathToUrl(path))); Disposer.dispose(oldDisposable); // dispose after the re-creating container to keep virtual file pointers from disposing and re-creating back // module roots already fire validity change events - addRootsFromModulesTo(recursive, flat); - return Pair.create(recursive, flat); + addRootsFromModulesTo(recursiveDirs, files); + return Pair.create(recursiveDirs, files); } private void addRootsFromModulesTo(@NotNull Set recursive, @NotNull Set flat) {