VCS: dirty scope manager, one refresh request for a group of dirty roots/files allowed

This commit is contained in:
Irina Chernushina
2008-10-10 10:37:10 +04:00
parent d0cf2a1190
commit 913d97bb21
18 changed files with 255 additions and 33 deletions
@@ -23,6 +23,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,8 +50,9 @@ public abstract class ChangeListManager implements ChangeListModification {
public abstract void scheduleUpdate();
public abstract void scheduleUpdate(boolean updateUnversionedFiles);
public abstract void invokeAfterUpdate(final Runnable afterUpdate, final boolean cancellable, final boolean silently, final String title,
final boolean synchronous);
public abstract void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title);
public abstract void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title,
final Consumer<VcsDirtyScopeManager> dirtyScopeManager);
public abstract boolean ensureUpToDate(boolean canBeCanceled);
public abstract List<LocalChangeList> getChangeListsCopy();
@@ -29,7 +29,7 @@ public class EnsureUpToDateFromNonAWTThread {
lock.notifyAll();
}
}
}, false, true, null, false);
}, InvokeAfterUpdateMode.SILENT, null);
while ((! myDone) && (! myProject.isDisposed())) {
try {
@@ -0,0 +1,31 @@
package com.intellij.openapi.vcs.changes;
public enum InvokeAfterUpdateMode {
SILENT(false, true, false),
BACKGROUND_CANCELLABLE(true, false, false),
BACKGROUND_NOT_CANCELLABLE(false, false, false),
SYNCHRONOUS_CANCELLABLE(true, false, true),
SYNCHRONOUS_NOT_CANCELLABLE(false, false, true);
private final boolean myCancellable;
private final boolean mySilently;
private final boolean mySynchronous;
InvokeAfterUpdateMode(final boolean cancellable, final boolean silently, final boolean synchronous) {
myCancellable = cancellable;
mySilently = silently;
mySynchronous = synchronous;
}
public boolean isCancellable() {
return myCancellable;
}
public boolean isSilently() {
return mySilently;
}
public boolean isSynchronous() {
return mySynchronous;
}
}
@@ -22,7 +22,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
@@ -82,4 +84,14 @@ public abstract class VcsDirtyScopeManager {
public abstract void dirDirtyRecursively(FilePath path);
public abstract List<VcsDirtyScope> retrieveScopes();
/**
* Requests an asynchronous file status update for all files specified and under the specified directories
*/
public abstract void filePathsDirty(@Nullable final Collection<FilePath> filesDirty, @Nullable final Collection<FilePath> dirsRecursivelyDirty);
/**
* Requests an asynchronous file status update for all files specified and under the specified directories
*/
public abstract void filesDirty(@Nullable final Collection<VirtualFile> filesDirty, @Nullable final Collection<VirtualFile> dirsRecursivelyDirty);
}
@@ -89,9 +89,7 @@ public class PatchApplier {
refreshIndirectlyAffected(indirectlyAffected);
final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
for (VirtualFile file : directlyAffected) {
vcsDirtyScopeManager.fileDirty(file);
}
vcsDirtyScopeManager.filesDirty(directlyAffected, null);
if ((myTargetChangeList != null) && (! directlyAffected.isEmpty())) {
ApplyPatchAction.moveChangesOfVsToList(myProject, directlyAffected, myTargetChangeList);
} else {
@@ -81,7 +81,7 @@ public abstract class AbstractCommonCheckinAction extends AbstractVcsAction {
CommitChangeListDialog.commitPaths(project, Arrays.asList(roots), initialSelection, getExecutor(project), null);
}
}
}, false, false, VcsBundle.message("waiting.changelists.update.for.show.commit.dialog.message"), true);
}, InvokeAfterUpdateMode.SYNCHRONOUS_NOT_CANCELLABLE, VcsBundle.message("waiting.changelists.update.for.show.commit.dialog.message"));
}
@Nullable
@@ -158,9 +158,13 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
*
* runnable is invoked on AWT thread
*/
public void invokeAfterUpdate(final Runnable afterUpdate, final boolean cancellable, final boolean silently, final String title,
final boolean synchronous) {
myUpdater.invokeAfterUpdate(afterUpdate, cancellable, silently, title, synchronous);
public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title) {
myUpdater.invokeAfterUpdate(afterUpdate, mode.isCancellable(), mode.isSilently(), title, mode.isSynchronous(), null);
}
public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title,
final Consumer<VcsDirtyScopeManager> dirtyScopeManagerFiller) {
myUpdater.invokeAfterUpdate(afterUpdate, mode.isCancellable(), mode.isSilently(), title, mode.isSynchronous(), dirtyScopeManagerFiller);
}
static class DisposedException extends RuntimeException {}
@@ -621,9 +625,9 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
}
for (VirtualFile file : files) {
VcsDirtyScopeManager.getInstance(myProject).fileDirty(file);
FileStatusManager.getInstance(myProject).fileStatusChanged(file);
}
VcsDirtyScopeManager.getInstance(myProject).filesDirty(files, null);
if (!list.isDefault()) {
// find the changes for the added files and move them to the necessary changelist
@@ -649,7 +653,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
ChangesViewManager.getInstance(myProject).scheduleRefresh();
}
}, false, false, VcsBundle.message("change.lists.manager.add.unversioned"), false);
}, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, VcsBundle.message("change.lists.manager.add.unversioned"));
} else {
ChangesViewManager.getInstance(myProject).scheduleRefresh();
}
@@ -105,11 +105,23 @@ public class UpdateRequestsQueue {
}
}
public void invokeAfterUpdate(final Runnable afterUpdate, final boolean cancellable, final boolean silently, final String title, final boolean synchronously) {
public void invokeAfterUpdate(final Runnable afterUpdate, final boolean cancellable, final boolean silently, final String title,
final boolean synchronously, final Consumer<VcsDirtyScopeManager> dirtyScopeManagerFiller) {
LOG.debug("invokeAfterUpdate for project: " + myProject.getName());
final CallbackData data = createCallbackWrapperRunnable(afterUpdate, cancellable, silently, title, synchronously);
VcsDirtyScopeManagerProxy managerProxy = null;
if (dirtyScopeManagerFiller != null) {
managerProxy = new VcsDirtyScopeManagerProxy();
dirtyScopeManagerFiller.consume(managerProxy);
}
synchronized (myLock) {
if (! myStopped) {
if (managerProxy != null) {
managerProxy.callRealManager(VcsDirtyScopeManager.getInstance(myProject));
}
myWaitingUpdateCompletionQueue.add(data.myCallback);
schedule(true);
}
@@ -17,10 +17,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author max
@@ -149,6 +146,58 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr
return !filePath.isDirectory() && parent != null && parent.equals(myProject.getBaseDir());
}
public void filePathsDirty(@Nullable final Collection<FilePath> filesDirty, @Nullable final Collection<FilePath> dirsRecursivelyDirty) {
boolean somethingDirty = false;
if (filesDirty != null) {
for (FilePath file : filesDirty) {
final AbstractVcs vcs = getVcsForDirty(file);
if (vcs != null) {
somethingDirty = true;
getScope(vcs).addDirtyFile(file);
}
}
}
if (dirsRecursivelyDirty != null) {
for (FilePath dir : dirsRecursivelyDirty) {
AbstractVcs vcs = getVcsForDirty(dir);
if (vcs != null) {
somethingDirty = true;
getScope(vcs).addDirtyDirRecursively(dir);
}
}
}
if (somethingDirty) {
myChangeListManager.scheduleUpdate();
}
}
public void filesDirty(@Nullable final Collection<VirtualFile> filesDirty, @Nullable final Collection<VirtualFile> dirsRecursivelyDirty) {
boolean somethingDirty = false;
if (filesDirty != null) {
for (VirtualFile file : filesDirty) {
final AbstractVcs vcs = getVcsForDirty(file);
if (vcs != null) {
somethingDirty = true;
getScope(vcs).addDirtyFile(new FilePathImpl(file));
}
}
}
if (dirsRecursivelyDirty != null) {
for (VirtualFile dir : dirsRecursivelyDirty) {
AbstractVcs vcs = getVcsForDirty(dir);
if (vcs != null) {
somethingDirty = true;
getScope(vcs).addDirtyDirRecursively(new FilePathImpl(dir));
}
}
}
if (somethingDirty) {
myChangeListManager.scheduleUpdate();
}
}
public void fileDirty(VirtualFile file) {
AbstractVcs vcs = getVcsForDirty(file);
if (vcs != null) {
@@ -0,0 +1,103 @@
package com.intellij.openapi.vcs.changes;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* to do not allow user to perform unknown activity under lock, pass mock manager
* use it later to call real manager under lock
*/
class VcsDirtyScopeManagerProxy extends VcsDirtyScopeManager {
private boolean myEverythingDirty;
// no grouping etc., let real manager do stuff
private final Set<VirtualFile> myVFiles;
private final Set<VirtualFile> myVDirs;
private final Set<FilePath> myFiles;
private final Set<FilePath> myDirs;
VcsDirtyScopeManagerProxy() {
myEverythingDirty = false;
myVFiles = new HashSet<VirtualFile>();
myVDirs = new HashSet<VirtualFile>();
myFiles = new HashSet<FilePath>();
myDirs = new HashSet<FilePath>();
}
public void markEverythingDirty() {
myEverythingDirty = true;
myVDirs.clear();
myVFiles.clear();
myDirs.clear();
myFiles.clear();
}
public void fileDirty(final VirtualFile file) {
myVFiles.add(file);
}
public void fileDirty(final FilePath file) {
myFiles.add(file);
}
public void dirDirtyRecursively(final VirtualFile dir, final boolean scheduleUpdate) {
myVDirs.add(dir);
}
public void dirDirtyRecursively(final VirtualFile dir) {
myVDirs.add(dir);
}
public void dirDirtyRecursively(final FilePath path) {
myDirs.add(path);
}
public List<VcsDirtyScope> retrieveScopes() {
throw new UnsupportedOperationException();
}
public void filePathsDirty(@Nullable final Collection<FilePath> filesDirty, @Nullable final Collection<FilePath> dirsRecursivelyDirty) {
if (filesDirty != null) {
myFiles.addAll(filesDirty);
}
if (dirsRecursivelyDirty != null) {
myDirs.addAll(dirsRecursivelyDirty);
}
}
public void filesDirty(@Nullable final Collection<VirtualFile> filesDirty, @Nullable final Collection<VirtualFile> dirsRecursivelyDirty) {
if (filesDirty != null) {
myVFiles.addAll(filesDirty);
}
if (dirsRecursivelyDirty != null) {
myVDirs.addAll(dirsRecursivelyDirty);
}
}
public void callRealManager(final VcsDirtyScopeManager manager) {
if (myEverythingDirty) {
manager.markEverythingDirty();
return;
}
for (FilePath file : myFiles) {
manager.fileDirty(file);
}
for (VirtualFile file : myVFiles) {
manager.fileDirty(file);
}
for (FilePath dir : myDirs) {
manager.dirDirtyRecursively(dir);
}
for (VirtualFile dir : myVDirs) {
manager.dirDirtyRecursively(dir);
}
}
}
@@ -52,6 +52,6 @@ public class CommitAction extends AnAction {
CommitChangeListDialog.commitChanges(project, Arrays.asList(changes), (LocalChangeList) list,
ChangeListManager.getInstance(project).getRegisteredExecutors(), true, null);
}
}, false, false, VcsBundle.message("waiting.changelists.update.for.show.commit.dialog.message"), true);
}, InvokeAfterUpdateMode.SYNCHRONOUS_NOT_CANCELLABLE, VcsBundle.message("waiting.changelists.update.for.show.commit.dialog.message"));
}
}
@@ -3,15 +3,16 @@ package com.intellij.openapi.vcs.changes.actions;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.openapi.vcs.changes.ui.RollbackChangesDialog;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import java.util.Arrays;
@@ -36,11 +37,11 @@ public class RollbackDialogAction extends AnAction {
browser.rebuildList();
}
}
}, false, false, VcsBundle.message("changes.action.rollback.text"), false);
}, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, VcsBundle.message("changes.action.rollback.text"));
}
public void update(AnActionEvent e) {
Change[] changes = e.getData(VcsDataKeys.CHANGES);
e.getPresentation().setEnabled(changes != null);
}
}
}
@@ -106,7 +106,7 @@ public class ShowDiffAction extends AnAction {
};
if (needsConvertion) {
ChangeListManager.getInstance(project).invokeAfterUpdate(performer, true, false, ourText, false);
ChangeListManager.getInstance(project).invokeAfterUpdate(performer, InvokeAfterUpdateMode.BACKGROUND_CANCELLABLE, ourText);
} else {
performer.run();
}
@@ -33,6 +33,7 @@ import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Processor;
@@ -197,7 +198,7 @@ public class ApplyPatchAction extends AnAction {
changeListManager.moveChangesTo(targetChangeList, changes.toArray(new Change[changes.size()]));
}
}, false, false, VcsBundle.message("change.lists.manager.move.changes.to.list"), false);
}, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, VcsBundle.message("change.lists.manager.move.changes.to.list"));
}
}
@@ -215,7 +216,7 @@ public class ApplyPatchAction extends AnAction {
}
changeListManager.moveChangesTo(targetChangeList, changes.toArray(new Change[changes.size()]));
}
}, false, false, VcsBundle.message("change.lists.manager.move.changes.to.list"), false);
}, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, VcsBundle.message("change.lists.manager.move.changes.to.list"));
}
}
@@ -248,4 +249,4 @@ public class ApplyPatchAction extends AnAction {
return request;
}
}
}
}
@@ -739,7 +739,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj
component.refresh();
}
}
}, false, true, "commit dialog", false); // title not shown for silently
}, InvokeAfterUpdateMode.SILENT, "commit dialog"); // title not shown for silently
}
public void saveState() {
@@ -24,6 +24,7 @@ import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
import com.intellij.openapi.vcs.checkin.CheckinHandler;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.ui.ConfirmationDialog;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -303,14 +304,20 @@ public class CommitHelper {
public void run() {
myAction.finish();
if (!myProject.isDisposed()) {
vcsRefresh();
LocalHistory.putSystemLabel(myProject, myActionName + ": " + myCommitMessage);
// after vcs refresh is completed, outdated notifiers should be removed if some exists...
ChangeListManager.getInstance(myProject).invokeAfterUpdate(new Runnable() {
public void run() {
CommittedChangesCache.getInstance(myProject).refreshIncomingChanges();
}
}, false, true, null, false);
}, InvokeAfterUpdateMode.SILENT, null, new Consumer<VcsDirtyScopeManager>() {
public void consume(final VcsDirtyScopeManager vcsDirtyScopeManager) {
for (FilePath path : myPathsToRefresh) {
vcsDirtyScopeManager.fileDirty(path);
}
}
});
LocalHistory.putSystemLabel(myProject, myActionName + ": " + myCommitMessage);
}
}
};
@@ -141,7 +141,7 @@ public class RollbackChangesDialog extends DialogWrapper {
final Runnable notifier = changeListManager.prepareForChangeDeletion(changes);
final Runnable afterRefresh = new Runnable() {
public void run() {
changeListManager.invokeAfterUpdate(notifier, false, true, "Refresh change lists after update", false);
changeListManager.invokeAfterUpdate(notifier, InvokeAfterUpdateMode.SILENT, "Refresh change lists after update");
}
};
@@ -427,15 +427,17 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
if (myActionInfo.canChangeFileStatus()) {
final VcsDirtyScopeManager myManager = VcsDirtyScopeManager.getInstance(myProject);
final List<VirtualFile> files = new ArrayList<VirtualFile>();
UpdateFilesHelper.iterateFileGroupFiles(myUpdatedFiles, new UpdateFilesHelper.Callback() {
public void onFile(final String filePath, final String groupId) {
@NonNls final String path = VfsUtil.pathToUrl(filePath.replace(File.separatorChar, '/'));
final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(path);
if (file != null) {
myManager.fileDirty(file);
files.add(file);
}
}
});
myManager.filesDirty(files, null);
}
final boolean updateSuccess = (! someSessionWasCancelled) && (myVcsExceptions.isEmpty());