From 61fc90b0cd3d4a0d9168781dec2e1887682ae56e Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 13 Mar 2012 12:43:03 +0400 Subject: [PATCH] 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). --- .../git4idea/repo/GitRepositoryManager.java | 56 +------ .../src/git4idea/roots/GitRootScanner.java | 140 ++++++++++++++++++ 2 files changed, 144 insertions(+), 52 deletions(-) create mode 100644 plugins/git4idea/src/git4idea/roots/GitRootScanner.java diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryManager.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryManager.java index be048ed1ef53..54aaa3010075 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryManager.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryManager.java @@ -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 myListeners = new HashSet(); @NotNull private final ReentrantReadWriteLock REPO_LOCK = new ReentrantReadWriteLock(); - - @NotNull private final Object ROOT_SCAN_STUB_OBJECT = new Object(); - @NotNull private final QueueProcessor myRootScanQueue = new QueueProcessor(new Consumer() { - @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 events) { - } - - @Override - public void after(@NotNull List 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(); - } - } } diff --git a/plugins/git4idea/src/git4idea/roots/GitRootScanner.java b/plugins/git4idea/src/git4idea/roots/GitRootScanner.java new file mode 100644 index 000000000000..047a62cc1c8d --- /dev/null +++ b/plugins/git4idea/src/git4idea/roots/GitRootScanner.java @@ -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 events) { + } + + @Override + public void after(@NotNull List 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; + } + +}