diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java index 18bc9195a4d2..c038b5058aa7 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java @@ -42,6 +42,7 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.util.SystemProperties; import com.intellij.util.containers.HashSet; import com.intellij.util.indexing.FileBasedIndexProjectHandler; +import com.intellij.util.indexing.UnindexedFilesUpdater; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -307,7 +308,12 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl { if (!myStartupActivityPerformed) return; if (myDoLogCachesUpdate) LOG.info(new Throwable("sync roots")); + DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject); + dumbService.queueTask(new UnindexedFilesUpdater(myProject, false)); + + if (myRootsChangeUpdaters.isEmpty()) return; + if (ourScheduleCacheUpdateInDumbMode) { dumbService.queueCacheUpdateInDumbMode(myRootsChangeUpdaters); } else { diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index 9d7a690cf27b..f37be9934cf7 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -18,7 +18,6 @@ package com.intellij.util.indexing; import com.intellij.AppTopics; import com.intellij.history.LocalHistory; -import com.intellij.ide.caches.CacheUpdater; import com.intellij.ide.util.DelegatingProgressIndicator; import com.intellij.lang.ASTNode; import com.intellij.notification.NotificationDisplayType; @@ -229,7 +228,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { LOG.info(e); } } - scheduleIndexRebuild(true); + scheduleIndexRebuild(); } }); @@ -1327,7 +1326,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { try { doClearIndex(indexId); if (!cleanupOnly) { - scheduleIndexRebuild(false); + scheduleIndexRebuild(); } } catch (StorageException e) { @@ -1365,16 +1364,9 @@ public class FileBasedIndexImpl extends FileBasedIndex { } } - private void scheduleIndexRebuild(boolean forceDumbMode) { + private static void scheduleIndexRebuild() { for (Project project : ProjectManager.getInstance().getOpenProjects()) { - final Set updatersToRun = Collections.singleton(new UnindexedFilesUpdater(project, this)); - final DumbServiceImpl service = DumbServiceImpl.getInstance(project); - if (forceDumbMode) { - service.queueCacheUpdateInDumbMode(updatersToRun); - } - else { - service.queueCacheUpdate(updatersToRun); - } + DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, false)); } } @@ -1671,6 +1663,10 @@ public class FileBasedIndexImpl extends FileBasedIndex { return myChangedFilesCollector.getNumberOfPendingInvalidations(); } + public int getChangedFileCount() { + return myChangedFilesCollector.getAllFilesToUpdate().size(); + } + @NotNull public Collection getFilesToUpdate(final Project project) { return ContainerUtil.findAll(myChangedFilesCollector.getAllFilesToUpdate(), new Condition() { diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java index c4e19e47cd51..8ca5421e09d6 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java @@ -37,6 +37,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.util.Consumer; import com.intellij.util.io.storage.HeavyProcessLatch; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,10 +59,18 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple startupManager.registerPreStartupActivity(new Runnable() { @Override public void run() { - final UnindexedFilesUpdater unindexedFilesUpdater = new UnindexedFilesUpdater(project, index); + // dumb mode should start before post-startup activities + // only when queueTask is called from UI thread, we can guarantee that + // when the method returns, the application has entered dumb mode + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + if (!project.isDisposed()) { + DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true)); + } + } + }); - startupManager.registerCacheUpdater(unindexedFilesUpdater); - rootManager.registerRootsChangeUpdater(unindexedFilesUpdater); myIndex.registerIndexableSet(FileBasedIndexProjectHandler.this, project); projectManager.addProjectManagerListener(project, new ProjectManagerAdapter() { private boolean removed = false; @@ -69,7 +78,6 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple public void projectClosing(Project project) { if (!removed) { removed = true; - rootManager.unregisterRootsChangeUpdater(unindexedFilesUpdater); myIndex.removeIndexableSet(FileBasedIndexProjectHandler.this); } } @@ -111,14 +119,13 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple @Nullable public static DumbModeTask createChangedFilesIndexingTask(final Project project) { final FileBasedIndexImpl index = (FileBasedIndexImpl)FileBasedIndex.getInstance(); - final Collection files = index.getFilesToUpdate(project); - if (files.isEmpty()) return null; - if (files.size() + index.getNumberOfPendingInvalidations() < 20 && !DumbService.isDumb(project)) { + if (index.getChangedFileCount() + index.getNumberOfPendingInvalidations() < 20 && !DumbService.isDumb(project)) { // the changed set is small, process it immediately without entering dumb mode // invalidation tasks are also processed and may take some time => take them into account try { HeavyProcessLatch.INSTANCE.processStarted(); + final Collection files = index.getFilesToUpdate(project); reindexRefreshedFiles(new EmptyProgressIndicator(), files, project, index); } finally { @@ -131,6 +138,7 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple return new DumbModeTask() { @Override public void performInDumbMode(@NotNull ProgressIndicator indicator) { + final Collection files = index.getFilesToUpdate(project); indicator.setIndeterminate(false); indicator.setText(IdeBundle.message("progress.indexing.updating")); reindexRefreshedFiles(indicator, files, project, index); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java index a4975f100d96..8f6b38811a0e 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java @@ -15,45 +15,43 @@ */ package com.intellij.util.indexing; -import com.intellij.ide.caches.CacheUpdater; +import com.intellij.ide.IdeBundle; import com.intellij.ide.caches.FileContent; +import com.intellij.ide.startup.impl.StartupManagerImpl; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.CacheUpdateRunner; +import com.intellij.openapi.project.DumbModeTask; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.CollectingContentIterator; -import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * @author Eugene Zhuravlev * @since Jan 29, 2008 */ -public class UnindexedFilesUpdater implements CacheUpdater { +public class UnindexedFilesUpdater extends DumbModeTask { private static final Logger LOG = Logger.getInstance("#com.intellij.util.indexing.UnindexedFilesUpdater"); - private final FileBasedIndexImpl myIndex; + private final FileBasedIndexImpl myIndex = (FileBasedIndexImpl)FileBasedIndex.getInstance(); private final Project myProject; - private final AtomicBoolean myFinishedUpdate = new AtomicBoolean(); - private long myStarted; + private final boolean myOnStartup; - public UnindexedFilesUpdater(final Project project, FileBasedIndexImpl index) { - myIndex = index; + public UnindexedFilesUpdater(final Project project, boolean onStartup) { myProject = project; + myOnStartup = onStartup; } - @Override - public int getNumberOfPendingUpdateJobs() { - return myIndex.getNumberOfPendingInvalidations(); - } + private void updateUnindexedFiles(ProgressIndicator indicator) { + indicator.setIndeterminate(true); + indicator.setText(IdeBundle.message("progress.indexing.scanning")); - @NotNull - @Override - public VirtualFile[] queryNeededFiles(@NotNull ProgressIndicator indicator) { - myIndex.filesUpdateStarted(myProject); CollectingContentIterator finder = myIndex.createContentIterator(indicator); long l = System.currentTimeMillis(); myIndex.iterateIndexableFiles(finder, myProject, indicator); @@ -61,35 +59,51 @@ public class UnindexedFilesUpdater implements CacheUpdater { LOG.info("Indexable files iterated in " + (System.currentTimeMillis() - l) + " ms"); List files = finder.getFiles(); + + if (myOnStartup) { + // full VFS refresh makes sense only after it's loaded, i.e. after scanning files to index is finished + ((StartupManagerImpl)StartupManager.getInstance(myProject)).scheduleInitialVfsRefresh(); + } + + if (files.isEmpty()) { + return; + } + + long started = System.currentTimeMillis(); LOG.info("Unindexed files update started: " + files.size() + " files to update"); - myFinishedUpdate.set(false); - myStarted = System.currentTimeMillis(); - return VfsUtilCore.toVirtualFileArray(files); + + indicator.setIndeterminate(false); + indicator.setText(IdeBundle.message("progress.indexing.updating")); + + indexFiles(indicator, files); + LOG.info("Unindexed files update done in " + (System.currentTimeMillis() - started) + " ms"); + } + + private void indexFiles(ProgressIndicator indicator, List files) { + CacheUpdateRunner.processFiles(indicator, true, files, myProject, new Consumer() { + @Override + public void consume(FileContent content) { + try { + myIndex.indexFileContent(myProject, content); + } + finally { + IndexingStamp.flushCache(content.getVirtualFile()); + } + } + }); } @Override - public void processFile(@NotNull FileContent fileContent) { + public void performInDumbMode(@NotNull ProgressIndicator indicator) { + myIndex.filesUpdateStarted(myProject); try { - myIndex.indexFileContent(myProject, fileContent); + updateUnindexedFiles(indicator); } - finally { - IndexingStamp.flushCache(fileContent.getVirtualFile()); - } - } - - @Override - public void updatingDone() { - if (myFinishedUpdate.compareAndSet(false, true)) { - myIndex.filesUpdateFinished(myProject); - LOG.info("Unindexed files update done in " + (System.currentTimeMillis() - myStarted) + " ms"); - } - } - - @Override - public void canceled() { - if (myFinishedUpdate.compareAndSet(false, true)) { - myIndex.filesUpdateFinished(myProject); + catch (ProcessCanceledException e) { LOG.info("Unindexed files update canceled"); + throw e; + } finally { + myIndex.filesUpdateFinished(myProject); } } } diff --git a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java index 8f144ca4a993..eee484f540e1 100644 --- a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java @@ -187,27 +187,34 @@ public class StartupManagerImpl extends StartupManagerEx { } }); - if (!app.isUnitTestMode() && !myProject.isDisposed()) { - if (!app.isHeadlessEnvironment()) { - checkProjectRoots(); - final long sessionId = VirtualFileManager.getInstance().asyncRefresh(null); - final MessageBusConnection connection = app.getMessageBus().connect(); - connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() { - @Override - public void afterProjectClosed(@NotNull Project project) { - RefreshQueue.getInstance().cancelSession(sessionId); - connection.disconnect(); - } - }); - } - else { - VirtualFileManager.getInstance().syncRefresh(); - } - } - Registry.get("ide.firstStartup").setValue(false); } + public void scheduleInitialVfsRefresh() { + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + if (myProject.isDisposed()) return; + + Application app = ApplicationManager.getApplication(); + if (!app.isHeadlessEnvironment()) { + checkProjectRoots(); + final long sessionId = VirtualFileManager.getInstance().asyncRefresh(null); + final MessageBusConnection connection = app.getMessageBus().connect(); + connection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener.Adapter() { + @Override + public void afterProjectClosed(@NotNull Project project) { + RefreshQueue.getInstance().cancelSession(sessionId); + connection.disconnect(); + } + }); + } + else { + VirtualFileManager.getInstance().syncRefresh(); + } + } + }); + } + private void checkProjectRoots() { LocalFileSystem fs = LocalFileSystem.getInstance(); if (!(fs instanceof LocalFileSystemImpl)) return; @@ -239,7 +246,27 @@ public class StartupManagerImpl extends StartupManagerEx { public void startCacheUpdate() { try { - DumbServiceImpl.getInstance(myProject).queueCacheUpdateInDumbMode(myCacheUpdaters); + DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject); + + if (!ApplicationManager.getApplication().isUnitTestMode()) { + // pre-startup activities have registered dumb tasks that load VFS (scanning files to index) + // only after these tasks pass does VFS refresh make sense + dumbService.queueTask(new DumbModeTask() { + @Override + public void performInDumbMode(@NotNull final ProgressIndicator indicator) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + if (!myProject.isDisposed()) { + scheduleInitialVfsRefresh(); + } + } + }); + } + }); + } + + dumbService.queueCacheUpdateInDumbMode(myCacheUpdaters); } catch (ProcessCanceledException e) { throw e;