mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
move empty directory detection from VirtualFilePointerManagerImpl to RefreshWorker to be less freezy (IDEA-CR-42309)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-14
@@ -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<FilePointerPartNode> myNodesToUpdateUrl = Collections.emptyList();
|
||||
private List<FilePointerPartNode> myNodesToFire = Collections.emptyList();
|
||||
|
||||
private static boolean isEmptyDir(@NotNull String path) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(path))) {
|
||||
return !stream.iterator().hasNext();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void before(@NotNull final List<? extends VFileEvent> 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) {
|
||||
|
||||
+21
-1
@@ -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<Path> stream = Files.newDirectoryStream(path)) {
|
||||
return stream.iterator().hasNext();
|
||||
}
|
||||
catch (IOException | SecurityException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -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;
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+4
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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());
|
||||
|
||||
+1
-1
@@ -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++) {
|
||||
|
||||
+3
-3
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user