IDEA-82632 Git: scan for roots only after VCS mappings are ready.

Move scanning of roots to a separate class: GitRootScanner.
Perform only 1 scan at a time (and reject others while scanning - not-scanning is not critical).
Scan only when project is initialized and VCS mappings are ready (i.e. the first VcsListener event is fired).
This commit is contained in:
Kirill Likhodedov
2012-03-13 12:43:03 +04:00
parent 7f1d5ac488
commit 61fc90b0cd
2 changed files with 144 additions and 52 deletions
@@ -15,30 +15,20 @@
*/
package git4idea.repo;
import com.intellij.ProjectTopics;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.QueueProcessor;
import com.intellij.util.messages.MessageBus;
import git4idea.GitUtil;
import git4idea.PlatformFacade;
import git4idea.roots.GitRootProblemNotifier;
import git4idea.roots.GitRootScanner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -60,16 +50,7 @@ public final class GitRepositoryManager extends AbstractProjectComponent impleme
@NotNull private final Set<GitRepositoryChangeListener> myListeners = new HashSet<GitRepositoryChangeListener>();
@NotNull private final ReentrantReadWriteLock REPO_LOCK = new ReentrantReadWriteLock();
@NotNull private final Object ROOT_SCAN_STUB_OBJECT = new Object();
@NotNull private final QueueProcessor<Object> myRootScanQueue = new QueueProcessor<Object>(new Consumer<Object>() {
@Override
public void consume(Object o) {
if (!myProject.isDisposed()) {
GitRootProblemNotifier.getInstance(myProject).rescanAndNotifyIfNeeded();
}
}
});
private GitRootScanner myRootScanner;
@Nullable
public static GitRepositoryManager getInstance(@NotNull Project project) {
@@ -85,16 +66,13 @@ public final class GitRepositoryManager extends AbstractProjectComponent impleme
@Override
public void initComponent() {
Disposer.register(myProject, this);
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() {
myRootScanner = new GitRootScanner(myProject, new DumbAwareRunnable() {
@Override
public void run() {
final MessageBus messageBus = myProject.getMessageBus();
final MyRepositoryCreationDeletionListener rootChangeListener = new MyRepositoryCreationDeletionListener();
messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, rootChangeListener);
messageBus.connect().subscribe(ProjectTopics.PROJECT_ROOTS, rootChangeListener);
updateRepositoriesCollection();
}
});
Disposer.register(this, myRootScanner);
}
@Override
@@ -211,8 +189,6 @@ public final class GitRepositoryManager extends AbstractProjectComponent impleme
finally {
REPO_LOCK.writeLock().unlock();
}
myRootScanQueue.add(ROOT_SCAN_STUB_OBJECT);
}
private static boolean gitRootOK(@NotNull VirtualFile root) {
@@ -233,28 +209,4 @@ public final class GitRepositoryManager extends AbstractProjectComponent impleme
return "GitRepositoryManager{myRepositories: " + myRepositories + '}';
}
private class MyRepositoryCreationDeletionListener implements BulkFileListener, ModuleRootListener {
@Override
public void before(@NotNull List<? extends VFileEvent> events) {
}
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
for (VFileEvent event : events) {
VirtualFile file = event.getFile();
if (file != null && file.getName().equalsIgnoreCase(".git") && file.isDirectory()) {
updateRepositoriesCollection();
}
}
}
@Override
public void beforeRootsChange(ModuleRootEvent event) {
}
@Override
public void rootsChanged(ModuleRootEvent event) {
updateRepositoriesCollection();
}
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.roots;
import com.intellij.ProjectTopics;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
/**
* @author Kirill Likhodedov
*/
public class GitRootScanner implements BulkFileListener, ModuleRootListener, Disposable, VcsListener {
@NotNull private final Runnable myExecuteAfterScan;
@NotNull private final GitRootProblemNotifier myRootProblemNotifier;
private volatile boolean myProjectIsInitialized;
private volatile boolean myMappingsAreReady;
private volatile boolean myScanning;
@NotNull private final Object SCAN_LOCK = new Object();
public GitRootScanner(@NotNull Project project, @NotNull Runnable executeAfterScan) {
myExecuteAfterScan = executeAfterScan;
StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
@Override
public void run() {
myProjectIsInitialized = true;
}
});
final MessageBus messageBus = project.getMessageBus();
messageBus.connect().subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, this);
messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, this);
messageBus.connect().subscribe(ProjectTopics.PROJECT_ROOTS, this);
myRootProblemNotifier = GitRootProblemNotifier.getInstance(project);
}
@Override
public void dispose() {
}
@Override
public void before(@NotNull List<? extends VFileEvent> events) {
}
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
for (VFileEvent event : events) {
VirtualFile file = event.getFile();
if (file != null && file.getName().equalsIgnoreCase(".git") && file.isDirectory()) {
scanIfReady();
}
}
}
@Override
public void beforeRootsChange(ModuleRootEvent event) {
}
@Override
public void rootsChanged(ModuleRootEvent event) {
scanIfReady();
}
@Override
public void directoryMappingChanged() {
myMappingsAreReady = true;
}
private void scanIfReady() {
if (readyToScan()) {
scan();
}
}
private void scan() {
if (myScanning) {
return;
}
if (SwingUtilities.isEventDispatchThread()) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
scanWithLock();
}
});
}
else {
scanWithLock();
}
}
private void scanWithLock() {
synchronized (SCAN_LOCK) {
if (myScanning) {
return;
}
myScanning = true;
myRootProblemNotifier.rescanAndNotifyIfNeeded();
myExecuteAfterScan.run();
myScanning = false;
}
}
private boolean readyToScan() {
return myMappingsAreReady && myProjectIsInitialized;
}
}