diff --git a/java/java-tests/testSrc/com/intellij/java/index/IndexTest.groovy b/java/java-tests/testSrc/com/intellij/java/index/IndexTest.groovy index 28266f7c4d7b..9a5423e310d9 100644 --- a/java/java-tests/testSrc/com/intellij/java/index/IndexTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/index/IndexTest.groovy @@ -952,7 +952,7 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename, filename2, true)) eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename2, filename, true)) eventList.add(new VFileDeleteEvent(null, file, true)) - eventList.add(new VFileCreateEvent(null, file.parent, filename, false, true)) + eventList.add(new VFileCreateEvent(null, file.parent, filename, false, true, false)) } IndexedFilesListener indexedFilesListener = ((FileBasedIndexImpl)FileBasedIndex.instance).changedFilesCollector diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java index 36cb06971b29..551c7ad5542a 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable; public class VFileCreateEvent extends VFileEvent { @NotNull private final VirtualFile myParent; private final boolean myDirectory; + private final boolean myEmptyDirectory; @NotNull private final String myChildName; private final FileAttributes myAttributes; private VirtualFile myCreatedFile; @@ -23,12 +24,14 @@ public class VFileCreateEvent extends VFileEvent { @NotNull VirtualFile parent, @NotNull String childName, final boolean isDirectory, - final boolean isFromRefresh) { + final boolean isFromRefresh, + boolean isEmptyDirectory) { super(requestor, isFromRefresh); myChildName = childName; myParent = parent; myDirectory = isDirectory; myAttributes = null; + myEmptyDirectory = isEmptyDirectory; } public VFileCreateEvent(Object requestor, @@ -41,6 +44,7 @@ public class VFileCreateEvent extends VFileEvent { myParent = parent; myDirectory = attributes.isDirectory(); myAttributes = attributes; + myEmptyDirectory = isEmptyDirectory; } @NotNull @@ -52,6 +56,13 @@ public class VFileCreateEvent extends VFileEvent { return myDirectory; } + /** + * @return true if the newly created file is a directory which has no children. + */ + public boolean isEmptyDirectory() { + return isDirectory() && myEmptyDirectory; + } + @NotNull public VirtualFile getParent() { return myParent; 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 85c87fb36972..6aba25136a9d 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 @@ -34,11 +34,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; import java.util.concurrent.ConcurrentMap; @@ -357,14 +352,6 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp private List myNodesToUpdateUrl = Collections.emptyList(); private List myNodesToFire = Collections.emptyList(); - private static boolean isEmptyDir(@NotNull String path) { - try (DirectoryStream stream = Files.newDirectoryStream(Paths.get(path))) { - return !stream.iterator().hasNext(); - } - catch (IOException e) { - return false; - } - } @Override public void before(@NotNull final List events) { ApplicationManager.getApplication().assertIsDispatchThread(); // guarantees no attempts to get read action lock under "this" lock @@ -384,7 +371,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp final VFileCreateEvent createEvent = (VFileCreateEvent)event; // when a new empty directory "/a/b" is created, there's no need to fire any deeper pointers like "/a/b/c/d.txt" - they're not created yet // OTOH when refresh found a new directory "/a/b" which is non-empty, we must fire deeper pointers because they may exist already - boolean fireSubdirectoryPointers = createEvent.isDirectory() && !isEmptyDir(createEvent.getPath()); + boolean fireSubdirectoryPointers = createEvent.isDirectory() && !createEvent.isEmptyDirectory(); addRelevantPointers(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents, fireSubdirectoryPointers); } else if (event instanceof VFileCopyEvent) { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java index 36d9b4b9b2b5..6c860cbd52cc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java @@ -34,6 +34,10 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; /** @@ -247,7 +251,10 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { final FileAttributes attributes = delegate.getAttributes(fake); if (attributes == null) return null; final String realName = delegate.getCanonicallyCasedName(fake); - final VFileCreateEvent event = new VFileCreateEvent(null, this, realName, attributes, true); + boolean isDirectory = attributes.isDirectory(); + boolean isEmptyDirectory = isDirectory && !hasChildren(Paths.get(fake.getPath())); + + final VFileCreateEvent event = new VFileCreateEvent(null, this, realName, attributes, true, isEmptyDirectory); RefreshQueue.getInstance().processSingleEvent(event); return findChild(realName); } @@ -693,4 +700,17 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } } } + + /** + * @return true if {@code path} represents a directory which has children. + */ + public static boolean hasChildren(@NotNull Path path) { + // make sure to not load all children + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + return stream.iterator().hasNext(); + } + catch (IOException | SecurityException e) { + return false; + } + } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/LocalFileSystemRefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/LocalFileSystemRefreshWorker.java index 89b604a54b81..968bacf72ea3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/LocalFileSystemRefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/LocalFileSystemRefreshWorker.java @@ -270,7 +270,8 @@ class LocalFileSystemRefreshWorker { boolean directory = attrs.isDirectory(); if (child == null) { // new file is created - myHelper.scheduleCreation(myFileOrDir.isDirectory() ? myFileOrDir : myFileOrDir.getParent(), name, convert(file, attrs)); + VirtualFile parent = myFileOrDir.isDirectory() ? myFileOrDir : myFileOrDir.getParent(); + myHelper.scheduleCreation(parent, name, file, convert(file, attrs)); return FileVisitResult.CONTINUE; } @@ -297,7 +298,8 @@ class LocalFileSystemRefreshWorker { oldIsSymlink != isLink || oldIsSpecial != isSpecial) { // symlink or directory or special changed myHelper.scheduleDeletion(child); - myHelper.scheduleCreation(myFileOrDir.isDirectory() ? myFileOrDir : myFileOrDir.getParent(), child.getName(), convert(file, attrs)); + VirtualFile parent = myFileOrDir.isDirectory() ? myFileOrDir : myFileOrDir.getParent(); + myHelper.scheduleCreation(parent, child.getName(), file, convert(file, attrs)); // ignore everything else child.markClean(); return FileVisitResult.CONTINUE; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index 670f8519b443..713d6a6236fb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -415,7 +415,7 @@ public class PersistentFSImpl extends PersistentFS implements BaseComponent, Dis @Override public VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile parent, @NotNull String dir) throws IOException { getDelegate(parent).createChildDirectory(requestor, parent, dir); - processEvent(new VFileCreateEvent(requestor, parent, dir, true, false)); + processEvent(new VFileCreateEvent(requestor, parent, dir, true, false, true)); final VirtualFile child = parent.findChild(dir); if (child == null) { @@ -428,7 +428,7 @@ public class PersistentFSImpl extends PersistentFS implements BaseComponent, Dis @Override public VirtualFile createChildFile(Object requestor, @NotNull VirtualFile parent, @NotNull String file) throws IOException { getDelegate(parent).createChildFile(requestor, parent, file); - processEvent(new VFileCreateEvent(requestor, parent, file, false, false)); + processEvent(new VFileCreateEvent(requestor, parent, file, false, false, false)); final VirtualFile child = parent.findChild(file); if (child == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java index 9bc3e763e19c..edd2f8f952fa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -192,7 +193,7 @@ public class RefreshWorker { String name = pair.first; FileAttributes childAttributes = pair.second; if (childAttributes != null) { - myHelper.scheduleCreation(dir, name, childAttributes); + myHelper.scheduleCreation(dir, name, Paths.get(dir.getPath(), name), childAttributes); } else { if (LOG.isTraceEnabled()) LOG.trace("[+] fs=" + fs + " dir=" + dir + " name=" + name); @@ -276,7 +277,7 @@ public class RefreshWorker { String name = pair.first; FileAttributes childAttributes = pair.second; if (childAttributes != null) { - myHelper.scheduleCreation(dir, name, childAttributes); + myHelper.scheduleCreation(dir, name, Paths.get(dir.getPath(), name), childAttributes); } } @@ -341,7 +342,7 @@ public class RefreshWorker { if (currentIsDirectory != upToDateIsDirectory || currentIsSymlink != upToDateIsSymlink || currentIsSpecial != upToDateIsSpecial) { myHelper.scheduleDeletion(child); - myHelper.scheduleCreation(parent, child.getName(), childAttributes); + myHelper.scheduleCreation(parent, child.getName(), Paths.get(parent.getPath(), child.getName()), childAttributes); return true; } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/VfsEventGenerationHelper.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/VfsEventGenerationHelper.java index 42275be2432c..9b224e8afb7c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/VfsEventGenerationHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/VfsEventGenerationHelper.java @@ -7,8 +7,10 @@ import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.events.*; +import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import org.jetbrains.annotations.NotNull; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -44,9 +46,11 @@ class VfsEventGenerationHelper { myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, oldTimestamp, newTimestamp, oldLength, newLength, true)); } - void scheduleCreation(@NotNull VirtualFile parent, @NotNull String childName, @NotNull FileAttributes attributes) { + void scheduleCreation(@NotNull VirtualFile parent, @NotNull String childName, @NotNull Path path, @NotNull FileAttributes attributes, boolean isDirectory) { + boolean isEmptyDir = isDirectory && !VirtualDirectoryImpl.hasChildren(path); + if (LOG.isTraceEnabled()) LOG.trace("create parent=" + parent + " name=" + childName + " attr=" + attributes); - myEvents.add(new VFileCreateEvent(null, parent, childName, attributes, true)); + myEvents.add(new VFileCreateEvent(null, parent, childName, attributes, true, isEmptyDir)); } void scheduleDeletion(@NotNull VirtualFile file) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilLightTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilLightTest.java index 2d1e204c96be..e4bd3ca1cd0b 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilLightTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilLightTest.java @@ -64,7 +64,7 @@ public class VfsUtilLightTest extends BareTestFixtureTestCase { VirtualFile jarRoot = VirtualFileManager.getInstance().findFileByUrl("jar://" + FileUtil.toSystemIndependentName(jarFile.getPath()) + "!/"); assertNotNull(jarRoot); - VFileCreateEvent event = new VFileCreateEvent(this, jarRoot, "x.txt", false, false); + VFileCreateEvent event = new VFileCreateEvent(this, jarRoot, "x.txt", false, false, false); assertEquals(FileUtil.toSystemIndependentName(jarFile.getPath()) + "!/x.txt", event.getPath()); } } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java index 8d5296ecb593..7b0489933d02 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java @@ -333,7 +333,7 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { events.clear(); TempFileSystem fs = TempFileSystem.getInstance(); IntStream.range(0, N) - .mapToObj(i -> new VFileCreateEvent(this, temp, i + ".txt", false, false)) + .mapToObj(i -> new VFileCreateEvent(this, temp, i + ".txt", false, false, false)) .peek(event -> { if (fs.findModelChild(temp, event.getChildName()) == null) { fs.createChildFile(this, temp, event.getChildName()); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerRootsTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerRootsTest.java index c02651cc8edf..d323bae20b8b 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerRootsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerRootsTest.java @@ -92,7 +92,7 @@ public class VirtualFilePointerRootsTest extends PlatformTestCase { myVirtualFilePointerManager.shelveAllPointersIn(() -> { for (int i = 0; i < 100_000; i++) { myVirtualFilePointerManager.create(VfsUtilCore.pathToUrl("/a/b/c/d/" + i), disposable, listener); - events.add(new VFileCreateEvent(this, temp, "xxx" + i, false, true)); + events.add(new VFileCreateEvent(this, temp, "xxx" + i, false, true, false)); } PlatformTestUtil.startPerformanceTest("vfp update", 7_000, () -> { for (int i = 0; i < 100; i++) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java index 287363a3afda..6906dd50042f 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java @@ -360,7 +360,7 @@ public class PersistentFsTest extends PlatformTestCase { checkEvents("Before:[VFileCreateEvent->xx.created, VFileDeleteEvent->file.txt]\n" + "After:[VFileCreateEvent->xx.created, VFileDeleteEvent->file.txt]\n", new VFileDeleteEvent(this, vFile, false), - new VFileCreateEvent(this, vFile.getParent(), "xx.created", false, false), + new VFileCreateEvent(this, vFile.getParent(), "xx.created", false, false, false), new VFileDeleteEvent(this, vFile, false)); } @@ -377,8 +377,8 @@ public class PersistentFsTest extends PlatformTestCase { "Before:[VFileDeleteEvent->c]\n" + "After:[VFileDeleteEvent->c]\n", new VFileDeleteEvent(this, vFile, false), - new VFileCreateEvent(this, vFile.getParent(), "xx.created", false, false), - new VFileCreateEvent(this, vFile.getParent(), "xx.created2", false, false), + new VFileCreateEvent(this, vFile.getParent(), "xx.created", false, false, false), + new VFileCreateEvent(this, vFile.getParent(), "xx.created2", false, false, false), new VFileDeleteEvent(this, vFile.getParent(), false)); }