diff --git a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointer.java b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointer.java index fb72a1fcfe5c..e53efe85ff78 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointer.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointer.java @@ -35,4 +35,12 @@ public interface VirtualFilePointer { String getPresentableUrl(); boolean isValid(); + + /** + * @return true if {@link VirtualFilePointerListener#validityChanged(VirtualFilePointer[])} + * should fired for every change beneath this directory (Used for jar directories in libraries) + */ + default boolean isRecursive() { + return false; + } } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java index 3d9c00938e8e..e66de2874b1f 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vfs.pointers; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -36,15 +37,19 @@ public interface VirtualFilePointerContainer { void remove(@NotNull VirtualFilePointer pointer); - @NotNull List getList(); + @NotNull + List getList(); void addAll(@NotNull VirtualFilePointerContainer that); - @NotNull String[] getUrls(); + @NotNull + String[] getUrls(); - @NotNull VirtualFile[] getFiles(); + @NotNull + VirtualFile[] getFiles(); - @NotNull VirtualFile[] getDirectories(); + @NotNull + VirtualFile[] getDirectories(); @Nullable VirtualFilePointer findByUrl(@NotNull String url); @@ -53,6 +58,15 @@ public interface VirtualFilePointerContainer { int size(); + /** + * For example, to read from the xml below, call {@code readExternal(myRootTag, "childElementName"); } + *
{@code
+   * 
+   *   
+   *   
+   * 
+   * }
+ */ void readExternal(@NotNull Element rootChild, @NotNull String childElementName) throws InvalidDataException; void writeExternal(@NotNull Element element, @NotNull String childElementName); @@ -61,7 +75,28 @@ public interface VirtualFilePointerContainer { void moveDown(@NotNull String url); - @NotNull VirtualFilePointerContainer clone(@NotNull Disposable parent); + @NotNull + VirtualFilePointerContainer clone(@NotNull Disposable parent); - @NotNull VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener); + @NotNull + VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener); + + /** + * Adds {@code directory} as a root of jar files. + * After that the {@link #getFiles()} and {@link #getUrls()} etc will return jar files in this directory + * (and, if {@code recursively} was set, the jar files in all-subdirectories). + */ + void addJarDirectory(@NotNull String directoryUrl, boolean recursively); + /** + * Removes {@code directory} from the roots of jar files. + * After that the {@link #getFiles()} and {@link #getUrls()} etc will not return jar files in this directory anymore. + * @return true if removed + */ + boolean removeJarDirectory(@NotNull String directoryUrl); + + /** + * Returns list of (directory url, isRecursive) which were added via {@link #addJarDirectory(String, boolean)} } + */ + @NotNull + List> getJarDirectories(); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerManager.java b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerManager.java index 15d1dcb5c417..e40e5406a175 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerManager.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerManager.java @@ -42,4 +42,10 @@ public abstract class VirtualFilePointerManager extends SimpleModificationTracke @NotNull public abstract VirtualFilePointerContainer createContainer(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener); + + @NotNull + public abstract VirtualFilePointer createDirectoryPointer(@NotNull String url, + boolean recursively, + @NotNull Disposable parent, + @NotNull VirtualFilePointerListener listener); } diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/CoreVirtualFilePointerManager.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/CoreVirtualFilePointerManager.java index 9333f7bb465d..527a3b746e7e 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/CoreVirtualFilePointerManager.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/CoreVirtualFilePointerManager.java @@ -59,4 +59,12 @@ public class CoreVirtualFilePointerManager extends VirtualFilePointerManager { public VirtualFilePointerContainer createContainer(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) { return new VirtualFilePointerContainerImpl(this, parent, listener); } + + @NotNull + @Override + public VirtualFilePointer createDirectoryPointer(@NotNull String url, + boolean recursively, + @NotNull Disposable parent, @NotNull VirtualFilePointerListener listener) { + return create(url, parent, listener); + } } 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 ea324d64073f..388cac2dd9a7 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 @@ -15,21 +15,28 @@ */ package com.intellij.openapi.vfs.impl; +import com.intellij.ide.highlighter.ArchiveFileType; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TraceableDisposable; import com.intellij.openapi.util.Trinity; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.io.URLUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -45,6 +52,8 @@ import java.util.List; class VirtualFilePointerContainerImpl extends TraceableDisposable implements VirtualFilePointerContainer, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer"); @NotNull private final List myList = ContainerUtil.createLockFreeCopyOnWriteList(); + @NotNull private final List myJarDirectories = ContainerUtil.createLockFreeCopyOnWriteList(); + @NotNull private final List myJarRecursiveDirectories = ContainerUtil.createLockFreeCopyOnWriteList(); @NotNull private final VirtualFilePointerManager myVirtualFilePointerManager; @NotNull private final Disposable myParent; private final VirtualFilePointerListener myListener; @@ -56,7 +65,6 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir VirtualFilePointerContainerImpl(@NotNull VirtualFilePointerManager manager, @NotNull Disposable parentDisposable, @Nullable VirtualFilePointerListener listener) { - //noinspection HardCodedStringLiteral super(TRACE_CREATION && !ApplicationInfoImpl.isInStressTest()); myVirtualFilePointerManager = manager; myParent = parentDisposable; @@ -64,8 +72,8 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir } @Override - public void readExternal(@NotNull final Element rootChild, @NotNull final String childElements) throws InvalidDataException { - final List urls = rootChild.getChildren(childElements); + public void readExternal(@NotNull final Element rootChild, @NotNull final String childName) throws InvalidDataException { + final List urls = rootChild.getChildren(childName); for (Element url : urls) { final String urlAttribute = url.getAttributeValue(URL_ATTR); if (urlAttribute == null) throw new InvalidDataException("path element without url"); @@ -119,16 +127,14 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir public void add(@NotNull VirtualFile file) { assert !myDisposed; dropCaches(); - final VirtualFilePointer pointer = create(file); - myList.add(pointer); + myList.add(create(file)); } @Override public void add(@NotNull String url) { assert !myDisposed; dropCaches(); - final VirtualFilePointer pointer = create(url); - myList.add(pointer); + myList.add(create(url)); } @Override @@ -154,6 +160,12 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir for (final VirtualFilePointer pointer : that.getList()) { myList.add(duplicate(pointer)); } + for (VirtualFilePointer jarDirectory : ((VirtualFilePointerContainerImpl)that).myJarDirectories) { + myJarDirectories.add(duplicate(jarDirectory)); + } + for (VirtualFilePointer jarRecursiveDirectory : ((VirtualFilePointerContainerImpl)that).myJarRecursiveDirectories) { + myJarRecursiveDirectories.add(duplicate(jarRecursiveDirectory)); + } } private void dropCaches() { @@ -181,7 +193,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @NotNull private Trinity cacheThings() { Trinity result; - if (myList.isEmpty()) { + if (myList.isEmpty() && myJarDirectories.isEmpty() && myJarRecursiveDirectories.isEmpty()) { result = EMPTY; } else { @@ -203,6 +215,40 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir } } } + for (VirtualFilePointer jarDirectoryPtr : myJarDirectories) { + VirtualFile jarDirectory = jarDirectoryPtr.getFile(); + if (jarDirectory != null) { + VirtualFile[] children = jarDirectory.getChildren(); + for (VirtualFile file : children) { + if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) { + VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); + if (jarRoot != null) { + cachedFiles.add(jarRoot); + cachedDirectories.add(file); + } + } + } + } + } + for (VirtualFilePointer jarDirectoryPtr : myJarRecursiveDirectories) { + VirtualFile jarDirectory = jarDirectoryPtr.getFile(); + if (jarDirectory != null) { + VfsUtilCore.visitChildrenRecursively(jarDirectory, new VirtualFileVisitor() { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) { + VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); + if (jarRoot != null) { + cachedFiles.add(jarRoot); + cachedDirectories.add(file); + return false; + } + } + return true; + } + }); + } + } VirtualFile[] directories = VfsUtilCore.toVirtualFileArray(cachedDirectories); VirtualFile[] files = allFilesAreDirs ? directories : VfsUtilCore.toVirtualFileArray(cachedFiles); String[] urlsArray = ArrayUtil.toStringArray(cachedUrls); @@ -229,7 +275,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @Nullable public VirtualFilePointer findByUrl(@NotNull String url) { assert !myDisposed; - for (VirtualFilePointer pointer : myList) { + for (VirtualFilePointer pointer : ContainerUtil.concat(myList, myJarDirectories, myJarRecursiveDirectories)) { if (url.equals(pointer.getUrl())) return pointer; } return null; @@ -243,16 +289,18 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @Override public int size() { - return myList.size(); + return myList.size()+myJarDirectories.size()+myJarRecursiveDirectories.size(); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof VirtualFilePointerContainerImpl)) return false; - final VirtualFilePointerContainerImpl virtualFilePointerContainer = (VirtualFilePointerContainerImpl)o; + VirtualFilePointerContainerImpl impl = (VirtualFilePointerContainerImpl)o; - return myList.equals(virtualFilePointerContainer.myList); + return myList.equals(impl.myList) && + myJarDirectories.equals(impl.myJarDirectories) && + myJarRecursiveDirectories.equals(impl.myJarRecursiveDirectories); } public int hashCode() { @@ -278,7 +326,9 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @NonNls @Override public String toString() { - return "VFPContainer: " + myList/*+"; parent:"+myParent*/; + return "VFPContainer: " + myList + + (myJarDirectories.isEmpty() ? "" : ", jars: "+myJarDirectories) + + (myJarRecursiveDirectories.isEmpty() ? "" : ", jars(recursively): "+myJarRecursiveDirectories); } @Override @@ -291,10 +341,16 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @NotNull public VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) { assert !myDisposed; - VirtualFilePointerContainer clone = myVirtualFilePointerManager.createContainer(parent, listener); + 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); + } return clone; } @@ -305,4 +361,27 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir kill(null); clear(); } + + @Override + public void addJarDirectory(@NotNull String directoryUrl, boolean recursively) { + VirtualFilePointer pointer = myVirtualFilePointerManager.createDirectoryPointer(directoryUrl, recursively, myParent, myListener); + (recursively ? myJarRecursiveDirectories : myJarDirectories).add(pointer); + dropCaches(); + } + + @Override + public boolean removeJarDirectory(@NotNull String directoryUrl) { + dropCaches(); + //noinspection NonShortCircuitBooleanExpression + return myJarDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)) + | myJarRecursiveDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)); + } + + @NotNull + @Override + public List> getJarDirectories() { + List> jars = ContainerUtil.map(myJarDirectories, ptr -> Pair.create(ptr.getUrl(), false)); + List> recJars = ContainerUtil.map(myJarRecursiveDirectories, ptr -> Pair.create(ptr.getUrl(), true)); + return ContainerUtil.concat(jars, recJars); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java index c681b4606244..afb22bbe3d9e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.newvfs.ManagingFS; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; @@ -51,11 +52,12 @@ class FilePointerPartNode { int pointersUnder; // number of alive pointers in this node plus all nodes beneath private static final VirtualFileManager ourFileManager = VirtualFileManager.getInstance(); - FilePointerPartNode(@NotNull String part, FilePointerPartNode parent, Pair fileAndUrl) { + FilePointerPartNode(@NotNull String part, FilePointerPartNode parent, Pair fileAndUrl, int pointersToStore) { this.part = part; this.parent = parent; children = EMPTY_ARRAY; myFileAndUrl = fileAndUrl; + pointersUnder = pointersToStore; } @Override @@ -67,8 +69,9 @@ class FilePointerPartNode { private int position(@Nullable VirtualFile parent, @Nullable CharSequence parentName, boolean separator, - @NotNull CharSequence childName, - @NotNull FilePointerPartNode[] outNode) { + @NotNull CharSequence childName, int childStart, int childEnd, + @NotNull FilePointerPartNode[] outNode, + @NotNull List dirs) { int partStart; if (parent == null) { partStart = 0; @@ -77,52 +80,77 @@ class FilePointerPartNode { else { VirtualFile gParent = parent.getParent(); CharSequence gParentName = gParent == null ? null : gParent.getNameSequence(); - partStart = position(gParent, gParentName, gParentName != null && !StringUtil.equals(gParentName, "/"), parentName, outNode); + partStart = position(gParent, gParentName, gParentName != null && !StringUtil.equals(gParentName, "/"), parentName, 0, parentName.length(), outNode, dirs); if (partStart == -1) return -1; } + FilePointerPartNode found = outNode[0]; boolean childSeparator = false; if (separator) { - if (partStart == outNode[0].part.length()) { + if (partStart == found.part.length()) { childSeparator = true; } else { - int sepIndex = indexOfFirstDifferentChar("/", 0, outNode[0].part, partStart); + int sepIndex = indexOfFirstDifferentChar("/", 0, found.part, partStart); if (sepIndex != 1) return -1; partStart++; } } - int index = indexOfFirstDifferentChar(childName, 0, outNode[0].part, partStart); + int index = indexOfFirstDifferentChar(childName, childStart, found.part, partStart); + + if (index == childEnd) { + addRecursiveDirectoryPtr(dirs); - if (index == childName.length()) { return partStart+index; } - if (partStart + index == outNode[0].part.length()) { + if (partStart + index-childStart == found.part.length()) { // go to children - for (FilePointerPartNode child : outNode[0].children) { - int childPos = child.position(null, null, childSeparator, childName.subSequence(index, childName.length()), outNode); - if (childPos != -1) return childPos; + for (FilePointerPartNode child : found.children) { + int childPos = child.position(null, null, childSeparator, childName, index, childEnd, outNode, dirs); + if (childPos != -1) { + addRecursiveDirectoryPtr(dirs); + + return childPos; + } } } // else there is no match return -1; } + private void addRecursiveDirectoryPtr(@NotNull List dirs) { + if(hasRecursiveDirectoryPointer() && (dirs.isEmpty() || dirs.get(dirs.size()-1) != this)) { + dirs.add(this); + } + } + // appends to "out" all nodes under this node whose path (beginning from this node) starts in prefix.subSequence(start), then parent.getPath(), then childName - void addPointersUnder(@Nullable VirtualFile parent, - boolean separator, - @NotNull CharSequence childName, - @NotNull List out) { - FilePointerPartNode[] outNode = new FilePointerPartNode[1]; + void addRelevantPointersFrom(@Nullable VirtualFile parent, + boolean separator, + @NotNull CharSequence childName, + @NotNull List out) { CharSequence parentName = parent == null ? null : parent.getNameSequence(); - int position = position(parent, parentName, separator, childName, outNode); + FilePointerPartNode[] outNode = new FilePointerPartNode[1]; + int position = position(parent, parentName, separator, childName, 0, childName.length(), outNode, out); if (position != -1) { FilePointerPartNode node = outNode[0]; addAllPointersUnder(node, out); } } + private boolean hasRecursiveDirectoryPointer() { + if (leaves == null) return false; + if (leaves instanceof VirtualFilePointer) { + return ((VirtualFilePointer)leaves).isRecursive(); + } + VirtualFilePointerImpl[] leaves = (VirtualFilePointerImpl[])this.leaves; + for (VirtualFilePointerImpl leaf : leaves) { + if (leaf.isRecursive()) return true; + } + return false; + } + private static void addAllPointersUnder(@NotNull FilePointerPartNode node, @NotNull List out) { if (node.leaves != null) { out.add(node); @@ -190,8 +218,7 @@ class FilePointerPartNode { } // cannot insert to children, create child node manually String pathRest = path.substring(index); - FilePointerPartNode newNode = new FilePointerPartNode(pathRest, this, fileAndUrl); - newNode.pointersUnder += pointersToStore; + FilePointerPartNode newNode = new FilePointerPartNode(pathRest, this, fileAndUrl, pointersToStore); children = ArrayUtil.append(children, newNode); pointersUnder += pointersToStore; return newNode; @@ -200,17 +227,13 @@ class FilePointerPartNode { // try to make "/" start the splitted part if (index > start + 1 && index != path.length() && path.charAt(index - 1) == '/') index--; String pathRest = path.substring(index); - FilePointerPartNode newNode = pathRest.isEmpty() ? this : new FilePointerPartNode(pathRest, this, fileAndUrl); - if (newNode != this) { - newNode.pointersUnder = pointersToStore; - } + FilePointerPartNode newNode = pathRest.isEmpty() ? this : new FilePointerPartNode(pathRest, this, fileAndUrl, pointersToStore); String commonPredecessor = StringUtil.first(part, index - start, false); - FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl); + FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl, pointersUnder); splittedAway.children = children; for (FilePointerPartNode child : children) { child.parent = splittedAway; } - splittedAway.pointersUnder = pointersUnder; splittedAway.useCount = useCount; splittedAway.associate(leaves, myFileAndUrl); useCount = 0; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java index bc07a2bd2742..135473ffec15 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java @@ -35,6 +35,7 @@ class VirtualFilePointerImpl extends TraceableDisposable implements VirtualFileP private static final boolean TRACE_CREATION = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode(); volatile FilePointerPartNode myNode; // null means disposed + boolean recursive; // true if the validityChanged() event should be fired for any change under this directory. Used for library jar directories. VirtualFilePointerImpl(@Nullable VirtualFilePointerListener listener) { super(TRACE_CREATION); @@ -132,4 +133,9 @@ class VirtualFilePointerImpl extends TraceableDisposable implements VirtualFileP int incrementUsageCount(int delta) { return myNode.incrementUsageCount(delta); } + + @Override + public boolean isRecursive() { + return recursive; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java index edec81f4021a..383f4dbad8ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java @@ -117,16 +117,16 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp @TestOnly synchronized VirtualFilePointer[] getPointersUnder(VirtualFile parent, String childName) { List nodes = new ArrayList<>(); - addPointersUnder(parent, true, childName, nodes); + addRelevantPointers(parent, true, childName, nodes); return toPointers(nodes); } - private void addPointersUnder(VirtualFile parent, - boolean separator, - @NotNull CharSequence childName, - @NotNull List out) { + private void addRelevantPointers(VirtualFile parent, + boolean separator, + @NotNull CharSequence childName, + @NotNull List out) { for (FilePointerPartNode root : myPointers.values()) { - root.addPointersUnder(parent, separator, childName, out); + root.addRelevantPointersFrom(parent, separator, childName, out); } } @@ -202,7 +202,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp } } // else url has come from VirtualFile.getPath() and is good enough - VirtualFilePointerImpl pointer = getOrCreate(listener, path, Pair.create(file, url)); + VirtualFilePointerImpl pointer = getOrCreate(path, Pair.create(file, url), listener); DelegatingDisposable.registerDisposable(parentDisposable, pointer); return pointer; } @@ -215,7 +215,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp VirtualFilePointerListener listener) { IdentityVirtualFilePointer pointer = myUrlToIdentity.get(url); if (pointer == null) { - pointer = new IdentityVirtualFilePointer(found, url,listener){ + pointer = new IdentityVirtualFilePointer(found, url,listener) { @Override public void dispose() { synchronized (VirtualFilePointerManagerImpl.this) { @@ -247,14 +247,13 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp } @NotNull - private VirtualFilePointerImpl getOrCreate(@Nullable VirtualFilePointerListener listener, - @NotNull String path, - @NotNull Pair fileAndUrl) { + private VirtualFilePointerImpl getOrCreate(@NotNull String path, + @NotNull Pair fileAndUrl, + @Nullable VirtualFilePointerListener listener) { FilePointerPartNode root = myPointers.get(listener); FilePointerPartNode node; if (root == null) { - root = new FilePointerPartNode(path, null, fileAndUrl); - root.pointersUnder++; + root = new FilePointerPartNode(path, null, fileAndUrl, 1); myPointers.put(listener, root); node = root; } @@ -286,7 +285,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp for (Map.Entry entry : myPointers.entrySet()) { FilePointerPartNode root = entry.getValue(); List left = new ArrayList<>(); - root.addPointersUnder(null, false, "", left); + root.addRelevantPointersFrom(null, false, "", left); List pointers = new ArrayList<>(); for (FilePointerPartNode node : left) { node.addAllPointersTo(pointers); @@ -316,7 +315,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp synchronized void addAllPointersTo(@NotNull Collection pointers) { List out = new ArrayList<>(); for (FilePointerPartNode root : myPointers.values()) { - root.addPointersUnder(null, false, "", out); + root.addRelevantPointersFrom(null, false, "", out); } for (FilePointerPartNode node : out) { node.addAllPointersTo(pointers); @@ -378,24 +377,23 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp for (VFileEvent event : events) { if (event instanceof VFileDeleteEvent) { final VFileDeleteEvent deleteEvent = (VFileDeleteEvent)event; - addPointersUnder(deleteEvent.getFile(), false, "", toFireEvents); - + addRelevantPointers(deleteEvent.getFile(), false, "", toFireEvents); } else if (event instanceof VFileCreateEvent) { final VFileCreateEvent createEvent = (VFileCreateEvent)event; - addPointersUnder(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents); + addRelevantPointers(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents); } else if (event instanceof VFileCopyEvent) { final VFileCopyEvent copyEvent = (VFileCopyEvent)event; - addPointersUnder(copyEvent.getNewParent(), true, copyEvent.getFile().getName(), toFireEvents); + addRelevantPointers(copyEvent.getNewParent(), true, copyEvent.getFile().getName(), toFireEvents); } else if (event instanceof VFileMoveEvent) { final VFileMoveEvent moveEvent = (VFileMoveEvent)event; VirtualFile eventFile = moveEvent.getFile(); - addPointersUnder(moveEvent.getNewParent(), true, eventFile.getName(), toFireEvents); + addRelevantPointers(moveEvent.getNewParent(), true, eventFile.getName(), toFireEvents); List nodes = new ArrayList<>(); - addPointersUnder(eventFile, false, "", nodes); + addRelevantPointers(eventFile, false, "", nodes); toFireEvents.addAll(nodes); // files deleted from eventFile and created in moveEvent.getNewParent() for (FilePointerPartNode node : nodes) { VirtualFilePointerImpl pointer = node.getAnyPointer(); @@ -411,10 +409,10 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp && !Comparing.equal(change.getOldValue(), change.getNewValue())) { VirtualFile eventFile = change.getFile(); VirtualFile parent = eventFile.getParent(); // e.g. for LightVirtualFiles - addPointersUnder(parent, true, change.getNewValue().toString(), toFireEvents); + addRelevantPointers(parent, true, change.getNewValue().toString(), toFireEvents); List nodes = new ArrayList<>(); - addPointersUnder(eventFile, false, "", nodes); + addRelevantPointers(eventFile, false, "", nodes); for (FilePointerPartNode node : nodes) { VirtualFilePointerImpl pointer = node.getAnyPointer(); VirtualFile file = pointer == null ? null : pointer.getFile(); @@ -567,6 +565,18 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp } } + @NotNull + @Override + public synchronized VirtualFilePointer createDirectoryPointer(@NotNull String url, + boolean recursively, + @NotNull Disposable parent, @NotNull VirtualFilePointerListener listener) { + VirtualFilePointer pointer = create(null, url, parent, listener); + if (pointer instanceof VirtualFilePointerImpl) { + ((VirtualFilePointerImpl)pointer).recursive = true; + } + return pointer; + } + @TestOnly synchronized int numberOfPointers() { int number = 0; @@ -585,4 +595,5 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp int numberOfCachedUrlToIdentity() { return myUrlToIdentity.size(); } + } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index 05a1cc656c29..604ec2b14fad 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -1004,4 +1004,24 @@ public class VirtualFilePointerTest extends PlatformTestCase { } LOG.debug("final i = " + i); } + + public void testDirectoryPointersWork() throws Exception { + final File dir = createTempDirectory(); + VirtualFile vDir = LocalFileSystem.getInstance().findFileByIoFile(dir); + assertNotNull(vDir); + VirtualFile deep = createChildDirectory(vDir, "deep"); + + LoggingListener listener = new LoggingListener(); + Disposable disposable = Disposer.newDisposable(); + VirtualFilePointer ptr = myVirtualFilePointerManager.createDirectoryPointer(vDir.getUrl(), false, disposable, listener); + + createChildData(vDir, "1"); + assertEquals("[before:true, after:true]", listener.getLog().toString()); + Disposer.dispose(disposable); + listener = new LoggingListener(); + myVirtualFilePointerManager.createDirectoryPointer(vDir.getUrl(), true, this.disposable, listener); + + createChildData(deep, "1"); + assertEquals("[before:true, after:true]", listener.getLog().toString()); + } }