local history performance: use calculated context (workspace file, fileindex) for batch processing of virtual file events

This commit is contained in:
Maxim.Mossienko
2016-10-13 11:20:43 +02:00
committed by Maxim.Mossienko
parent babc91c239
commit a35b3d83a3
4 changed files with 115 additions and 13 deletions
@@ -25,8 +25,12 @@ import org.jetbrains.annotations.Nullable;
public abstract class LocalHistory {
public static final Object VFS_EVENT_REQUESTOR = new Object();
private static class LocalHistoryHolder {
static final LocalHistory ourInstance = ApplicationManager.getApplication().getComponent(LocalHistory.class);
}
public static LocalHistory getInstance() {
return ApplicationManager.getApplication().getComponent(LocalHistory.class);
return LocalHistoryHolder.ourInstance;
}
public abstract LocalHistoryAction startAction(@Nullable String name);
@@ -40,6 +40,7 @@ import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.encoding.EncodingRegistry;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
@@ -66,21 +67,79 @@ public class IdeaGateway {
if (!f.isDirectory() && StringUtil.endsWith(f.getNameSequence(), ".class")) return false;
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
LocalHistoryImpl.getInstanceImpl().dispatchPendingEvents();
VersionedFilterData versionedFilterData;
VfsEventDispatchContext vfsEventDispatchContext = ourCurrentEventDispatchContext.get();
if (vfsEventDispatchContext != null) {
versionedFilterData = vfsEventDispatchContext.myFilterData;
if (versionedFilterData == null) versionedFilterData = vfsEventDispatchContext.myFilterData = new VersionedFilterData();
} else {
versionedFilterData = new VersionedFilterData();
}
boolean isInContent = false;
for (Project each : openProjects) {
if (each.isDefault()) continue;
if (!each.isInitialized()) continue;
if (Comparing.equal(each.getWorkspaceFile(), f)) return false;
ProjectFileIndex index = ProjectRootManager.getInstance(each).getFileIndex();
int numberOfOpenProjects = versionedFilterData.myOpenedProjects.size();
for (int i = 0; i < numberOfOpenProjects; ++i) {
if (Comparing.equal(versionedFilterData.myWorkspaceFiles.get(i), f)) return false;
ProjectFileIndex index = versionedFilterData.myProjectFileIndices.get(i);
if (index.isExcluded(f)) return false;
isInContent |= index.isInContent(f);
}
if (shouldBeInContent && !isInContent) return false;
// optimisation: FileTypeManager.isFileIgnored(f) already checked inside ProjectFileIndex.isIgnored()
return openProjects.length != 0 || !FileTypeManager.getInstance().isFileIgnored(f);
return numberOfOpenProjects != 0 || !FileTypeManager.getInstance().isFileIgnored(f);
}
private static final ThreadLocal<VfsEventDispatchContext> ourCurrentEventDispatchContext = new ThreadLocal<>();
private static class VfsEventDispatchContext {
final List<? extends VFileEvent> myEvents;
final boolean myBeforeEvents;
final VfsEventDispatchContext myPreviousContext;
VersionedFilterData myFilterData;
VfsEventDispatchContext(List<? extends VFileEvent> events, boolean beforeEvents, VfsEventDispatchContext context) {
myEvents = events;
myBeforeEvents = beforeEvents;
myPreviousContext = context;
}
public void close() {
ourCurrentEventDispatchContext.set(myPreviousContext);
}
}
public void runWithVfsEventsDispatchContext(List<? extends VFileEvent> events, boolean beforeEvents, Runnable action) {
VfsEventDispatchContext vfsEventDispatchContext = new VfsEventDispatchContext(events, beforeEvents, ourCurrentEventDispatchContext.get());
ourCurrentEventDispatchContext.set(vfsEventDispatchContext);
try {
action.run();
} finally {
vfsEventDispatchContext.close();
}
}
private static class VersionedFilterData {
final List<Project> myOpenedProjects = new ArrayList<>();
final List<ProjectFileIndex> myProjectFileIndices = new ArrayList<>();
final List<VirtualFile> myWorkspaceFiles = new ArrayList<>();
VersionedFilterData() {
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
for (Project each : openProjects) {
if (each.isDefault()) continue;
if (!each.isInitialized()) continue;
myWorkspaceFiles.add(each.getWorkspaceFile());
myOpenedProjects.add(each);
myProjectFileIndices.add(ProjectRootManager.getInstance(each).getFileIndex());
}
}
}
public boolean areContentChangesVersioned(@NotNull VirtualFile f) {
@@ -23,10 +23,16 @@ import com.intellij.openapi.command.CommandListener;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener {
import java.util.List;
public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener,
BulkFileListener {
private static final Key<Boolean> WAS_VERSIONED_KEY =
Key.create(LocalHistoryEventDispatcher.class.getSimpleName() + ".WAS_VERSIONED_KEY");
@@ -193,4 +199,22 @@ public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements V
private boolean areContentChangesVersioned(VirtualFileEvent e) {
return myGateway.areContentChangesVersioned(e.getFile());
}
@Override
public void before(@NotNull List<? extends VFileEvent> events) {
myGateway.runWithVfsEventsDispatchContext(events, true, () -> {
for (VFileEvent event : events) {
BulkVirtualFileListenerAdapter.fireBefore(this, event);
}
});
}
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
myGateway.runWithVfsEventsDispatchContext(events, false, () -> {
for (VFileEvent event : events) {
BulkVirtualFileListenerAdapter.fireAfter(this, event);
}
});
}
}
@@ -35,6 +35,8 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,6 +49,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.history.integration.LocalHistoryUtil.findRevisionIndexToRevert;
public class LocalHistoryImpl extends LocalHistory implements ApplicationComponent {
private final MessageBus myBus;
private MessageBusConnection myConnection;
private ChangeList myChangeList;
private LocalHistoryFacade myVcs;
private IdeaGateway myGateway;
@@ -60,6 +64,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
return (LocalHistoryImpl)getInstance();
}
public LocalHistoryImpl(@NotNull MessageBus bus) {
myBus = bus;
}
@Override
public void initComponent() {
if (!ApplicationManager.getApplication().isUnitTestMode() && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
@@ -89,8 +97,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
CommandProcessor.getInstance().addCommandListener(myEventDispatcher);
myConnection = myBus.connect();
myConnection.subscribe(VirtualFileManager.VFS_CHANGES, myEventDispatcher);
VirtualFileManager fm = VirtualFileManager.getInstance();
fm.addVirtualFileListener(myEventDispatcher);
fm.addVirtualFileManagerListener(myEventDispatcher);
if (ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode()) {
@@ -126,8 +136,9 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
long period = Registry.intValue("localHistory.daysToKeep") * 1000L * 60L * 60L * 24L;
myConnection.disconnect();
myConnection = null;
VirtualFileManager fm = VirtualFileManager.getInstance();
fm.removeVirtualFileListener(myEventDispatcher);
fm.removeVirtualFileManagerListener(myEventDispatcher);
CommandProcessor.getInstance().removeCommandListener(myEventDispatcher);
@@ -143,6 +154,10 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
ShutDownTracker.getInstance().unregisterShutdownTask(myShutdownTask);
}
protected void dispatchPendingEvents() {
myConnection.deliverImmediately();
}
@TestOnly
public void cleanupForNextTest() {
disposeComponent();